Tengo 2 eventos externos, input y keydown para un elemento de entrada, cuando se activa la entrada, llamará a setInput en el componente React. Cuando se dispara el evento keydown para arrowDown , idealmente el valor de entrada debería ser el establecido dentro de handleInput , pero está vacío, ¿me pierdo algo?
const SearchBox : React.FunctionComponent<ISearchProps> = React.memo((props: ISearchProps) => { const [input, setInput] = React.useState({value: '', redirect: false}); useEffect(() => { const container = document.querySelector('.xxx'); container.addEventListener('input', handleInput); container.addEventListener('keydown', handleKeyboardEvent); }, []); const handleInput = (e) => { setInput({value: e.currentTarget.value, redirect: false}); } const handleKeyboardEvent = (e) => { switch (e.key) { case 'ArrowDown': console.log(input); setActiveSuggestionIndex(activeSuggestionIndex === suggestions.length - 1 ? 0 : activeSuggestionIndex + 1); e.preventDefault(); break; default: break; } } } Este es un problema de recintos de estado obsoletos en una función de devolución de llamada. El valor de estado de input inicial de { value: '', redirect: false } se cierra en la copia de handleKeyboardEvent del ciclo de procesamiento inicial. Nunca se actualizará.
useEffect para almacenar en caché una copia del valor Use una referencia React y un useEffect adicional para almacenar en caché un valor al que se accederá de forma asíncrona en la devolución de llamada.
const [input, setInput] = React.useState({ value: '', redirect: false }); const inputRef = React.useRef(input); // <-- state cache useEffect(() => { const container = document.querySelector('.xxx'); container.addEventListener('input', handleInput); container.addEventListener('keydown', handleKeyboardEvent); return () => { container.removeEventListener('input', handleInput); container.removeEventListener('keydown', handleKeyboardEvent); }; }, []); useEffect(() => { inputRef.current = input; // <-- update state cache value }, [input]); const handleInput = (e) => { setInput({ value: e.currentTarget.value, redirect: false }); }; const handleKeyboardEvent = (e) => { switch (e.key) { case 'ArrowDown': console.log(inputRef); // <-- read current state cache value setActiveSuggestionIndex(activeSuggestionIndex => activeSuggestionIndex === suggestions.length - 1 ? 0 : activeSuggestionIndex + 1 ); e.preventDefault(); break; default: break; } };useEffect con función de dependencia y limpieza Dado que también debería devolver una función de limpieza de useEffect para eliminar los detectores de eventos al desmontar, agregue el estado de input a la matriz de dependencia ( y cualquier otra dependencia faltante de la que se pueda quejar el linter ) para que cuando el estado de input se actualice, el el valor del estado actual se vuelve a encerrar en el alcance de devolución de llamada.
useEffect(() => { const handleInput = (e) => { setInput({ value: e.currentTarget.value, redirect: false }); }; const handleKeyboardEvent = (e) => { switch (e.key) { case 'ArrowDown': console.log(input); setActiveSuggestionIndex(activeSuggestionIndex => 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 () => { container.removeEventListener('input', handleInput); container.removeEventListener('keydown', handleKeyboardEvent); }; }, [input]);