反应挂钩键盘导航

时间:2019-07-08 23:27:36

标签: reactjs react-hooks use-effect

我正在尝试使用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];

    }

1 个答案:

答案 0 :(得分:0)

要发布我的解决方案,最终使用状态和引用的混合体。对于大型列表,仍然可以听到性能更好的解决方案。

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} ...>