#javascript #reactjs #react-hooks #dom-events
#язык JavaScript #реагирует на #реагируют-крючки #дом-события
Вопрос:
У меня есть 2 внешних события, input
и keydown
для элемента ввода при запуске ввода он вызовет setInput
компонент React. Когда keydown
событие для arrowDown
запуска, в идеале входное значение должно быть задано внутри handleInput
, но оно пустое, я что-то пропустил?
const SearchBox : React.FunctionComponentlt;ISearchPropsgt; = React.memo((props: ISearchProps) =gt; { const [input, setInput] = React.useState({value: '', redirect: false}); useEffect(() =gt; { const container = document.querySelector('.xxx'); container.addEventListener('input', handleInput); container.addEventListener('keydown', handleKeyboardEvent); }, []); const handleInput = (e) =gt; { setInput({value: e.currentTarget.value, redirect: false}); } const handleKeyboardEvent = (e) =gt; { switch (e.key) { case 'ArrowDown': console.log(input); setActiveSuggestionIndex(activeSuggestionIndex === suggestions.length - 1 ? 0 : activeSuggestionIndex 1); e.preventDefault(); break; default: break; } } }
Комментарии:
1. То, как вы вызываете события, не очень похоже на реакцию JS, но попробуйте переключиться на это:
setInput({...input, value: e.currentTarget.value, redirect: false});
Ответ №1:
Вопрос
Это проблема устаревших вложений состояния в функции обратного вызова. Значение начального input
состояния { value: '', redirect: false }
закрывается в копии handleKeyboardEvent
из начального цикла рендеринга. Он никогда не будет обновляться.
Решения
Используйте React ref и useEffect
для кэширования копии значения
Используйте ссылку React и дополнительный useEffect
крюк для кэширования значения, доступ к которому будет осуществляться асинхронно при обратном вызове.
const [input, setInput] = React.useState({ value: '', redirect: false }); const inputRef = React.useRef(input); // lt;-- state cache useEffect(() =gt; { const container = document.querySelector('.xxx'); container.addEventListener('input', handleInput); container.addEventListener('keydown', handleKeyboardEvent); return () =gt; { container.removeEventListener('input', handleInput); container.removeEventListener('keydown', handleKeyboardEvent); }; }, []); useEffect(() =gt; { inputRef.current = input; // lt;-- update state cache value }, [input]); const handleInput = (e) =gt; { setInput({ value: e.currentTarget.value, redirect: false }); }; const handleKeyboardEvent = (e) =gt; { switch (e.key) { case 'ArrowDown': console.log(inputRef); // lt;-- read current state cache value setActiveSuggestionIndex(activeSuggestionIndex =gt; activeSuggestionIndex === suggestions.length - 1 ? 0 : activeSuggestionIndex 1 ); e.preventDefault(); break; default: break; } };
Используйте useEffect
с функцией зависимости и очистки
Поскольку вы также должны уже возвращать функцию очистки из списка useEffect
для удаления прослушивателей событий при отключении, добавьте input
состояние в массив зависимостей (и любые другие отсутствующие зависимости, на которые может пожаловаться линтер), чтобы при input
обновлении состояния текущее значение состояния было повторно заключено в область обратного вызова.
useEffect(() =gt; { const handleInput = (e) =gt; { setInput({ value: e.currentTarget.value, redirect: false }); }; const handleKeyboardEvent = (e) =gt; { switch (e.key) { case 'ArrowDown': console.log(input); setActiveSuggestionIndex(activeSuggestionIndex =gt; activeSuggestionIndex === suggestions.length - 1 ? 0 : activeSuggestionIndex 1 ); e.preventDefault(); break; default: break; } }; const container = document.querySelector('.xxx'); container.addEventListener('input', handleInput); container.addEventListener('keydown', handleKeyboardEvent); return () =gt; { container.removeEventListener('input', handleInput); container.removeEventListener('keydown', handleKeyboardEvent); }; }, [input]);
Комментарии:
1. Спасибо, это работает, но, похоже, не перерисовывается после перехода в useRef, есть ли способ перерисовки?
2. @Лей Добро пожаловать. Не могли бы вы уточнить для меня, что не является повторной передачей? Использование ссылок React выходит за рамки жизненного цикла компонентов react, другими словами, ссылки похожи на изменяемые «корзины», в которых вы можете хранить любое значение, сохраняющееся в циклах рендеринга.
3. спасибо за объяснение, интересно, могу ли я использовать useRef, а также посмотреть, есть ли способ повторно отобразить DOM (например, useState)? Как и повторный рендеринг вызова вручную, поскольку этот обновленный «ввод» должен быть повторно отрисован, я не хочу хранить 2 копии (один пользователь, другой использует useState).
4. @Lei О, ты снял
input
штат сuseState
крючка? Вам все еще это нужно, чтобы вы могли обновлять состояние и запускать повторные отправители.5. @Lei Есть альтернатива, которую я могу показать вам, если вам интересно, для полноты картины, я полагаю.