我正在尝试使用React-Hooks实现列表的键盘导航。
重要 - 此列表可以根据搜索收缩/成长。
我的问题是围绕Enter
密钥,该密钥应运行一些回调。activeCursor
不更改,我理解的是,因为它不在useEffect
数组中,但是我如何在handleKeyPress
中获得当前状态,而无需重新运行useEffect
?
另外,理想情况下,我只想在坐骑( []
(上运行我的 useEffect
,但是由于 filteredMessages
更改,所以我必须重新打电话给它,这也很奇怪,因为它的 eventListeners
,所以我什至不确定会发生什么每次给他们...
const useKeyboardNavigation = (filteredMessages, onMessageSelection) => {
const [activeCursor, setActiveCursor] = React.useState(0);
const size = filteredMessages.length
const handleKeyPress = (event) => {
if (event.key === 'ArrowDown') {
setActiveCursor(prev => prev < size ? prev + 1 : 0)
}
else if (event.key === 'ArrowUp') {
setActiveCursor(prev => prev > 0 ? prev - 1 : size)
}
else if (event.key === 'Enter') {
const msg = filteredMessages[activeCursor] // ??? Always 0
onMessageSelection(msg)
}
};
React.useEffect(
() => {
// Each time the list changes I reset the cursor
setActiveCursor(0)
document.addEventListener('keydown', handleKeyPress);
return () => document.removeEventListener('keydown', handleKeyPress);
},
[filteredMessages]
);
return [activeCursor, setActiveCursor];
}
要发布我的解决方案,最终使用状态和参考的混合物。对于大列表,仍然可以听到具有更好性能的解决方案。
const useKeyboardNavigation = (size: number) => {
const [activeCursor, setActiveCursor] = React.useState(0);
const handleKeyPress = event => {
if (event.key === 'ArrowDown') {
setActiveCursor(prev => (prev < size ? prev + 1 : 0));
} else if (event.key === 'ArrowUp') {
setActiveCursor(prev => (prev > 0 ? prev - 1 : size));
}
};
// Reset when size changes
React.useEffect(() => setActiveCursor(0), [size]);
React.useEffect(
() => {
document.addEventListener('keydown', handleKeyPress);
return () => document.removeEventListener('keydown', handleKeyPress);
},
[size, activeCursor]
);
return [activeCursor, setActiveCursor];
};
用法:
const [activeCursor, setActiveCursor] = useKeyboardNavigation(messages.length);
然后,当我渲染每个ListItem(使用messages.map
(时:
const useFocus = (isActive: boolean) => {
const itemRef = React.useRef<HTMLDivElement>(null);
React.useEffect(
() => {
isActive && itemRef && itemRef.current && itemRef.current.focus();
},
[isActive]
);
return itemRef;
};
用法:
const myRef = useFocus(activeCursor === index);
<li ref={myRef} ...>