Реакция 2 внешние события не могут обмениваться данными о крючках

#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 Есть альтернатива, которую я могу показать вам, если вам интересно, для полноты картины, я полагаю.