Реактив хуки клавиатурной навигации - PullRequest
1 голос
/ 09 июля 2019

Я пытаюсь реализовать клавиатурную навигацию по списку с помощью реагирующих хуков.

важный - Этот список может уменьшаться / увеличиваться в зависимости от результатов поиска.

Моя проблема связана с клавишей 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 голосов
/ 09 июля 2019

Собираюсь опубликовать мое решение, в итоге я использовал смесь состояния и ссылок. Было бы все еще открыто для решения с лучшей производительностью для больших списков.

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} ...>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...