【问题标题】:React hooks keyboard navigationReact 钩子键盘导航
【发布时间】:2019-11-18 11:37:23
【问题描述】:

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

    }

【问题讨论】:

  • 鉴于handleKeyPress 依赖于filteredMessagesactiveCursor,那么当它们发生变化时必须重新创建它,并且由于useEffect 使用handleKeyPress 那么它还需要重新运行 - 什么是问题?
  • 在课堂上,我可以通过调用this.state 来获取handleKeyPress 中的状态。有了钩子,我现在每次光标移动时都必须重新添加 eventListener
  • 不一定,您可以使用ref 来跟踪当前光标。但是,当光标改变时是否需要取消挂钩/重新预订真的很重要吗?您遇到问题了吗?
  • 正如@James 提到的,您可以使用ref 获取当前状态(查看 Dan Abramov 的博客,A Complete Guide to Use Effect - Swimming against the tide)部分。我也有兴趣访问当前状态值。

标签: reactjs react-hooks use-effect


【解决方案1】:

要发布我的解决方案,最终使用了 state 和 refs 的混合。仍然愿意听取对大型列表具有更好性能的解决方案。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-03
    • 2011-07-06
    • 1970-01-01
    • 2016-06-21
    • 2015-12-06
    相关资源
    最近更新 更多