React 2 外部事件无法共享挂钩数据
React 2 ouside events cannot share hook data
我有 2 个外部事件,input
和 keydown
用于输入元素,当输入触发时,它将在 React 组件中调用 setInput
。当 arrowDown
的 keydown
事件被触发时,理想情况下输入值应该是 handleInput
中设置的值,但它是空的,我错过了什么吗?
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;
}
}
}
问题
这是回调函数中陈旧状态封装的问题。 { value: '', redirect: false }
的初始 input
状态值在 handleKeyboardEvent
的副本中从初始渲染周期关闭。永远不会更新。
解决方案
使用 React ref 和 useEffect
缓存值的副本
使用 React ref 和额外的 useEffect
钩子来缓存要在回调中异步访问的值。
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
与依赖和清理功能一起使用
由于您还应该已经从 useEffect
返回清理函数以在卸载时删除事件侦听器,因此将 input
状态添加到依赖项数组( 和任何linter 可能会抱怨其他缺少的依赖项 ),因此当 input
状态更新时,当前状态值会重新包含在回调范围中。
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]);
我有 2 个外部事件,input
和 keydown
用于输入元素,当输入触发时,它将在 React 组件中调用 setInput
。当 arrowDown
的 keydown
事件被触发时,理想情况下输入值应该是 handleInput
中设置的值,但它是空的,我错过了什么吗?
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;
}
}
}
问题
这是回调函数中陈旧状态封装的问题。 { value: '', redirect: false }
的初始 input
状态值在 handleKeyboardEvent
的副本中从初始渲染周期关闭。永远不会更新。
解决方案
使用 React ref 和 useEffect
缓存值的副本
使用 React ref 和额外的 useEffect
钩子来缓存要在回调中异步访问的值。
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
与依赖和清理功能一起使用
由于您还应该已经从 useEffect
返回清理函数以在卸载时删除事件侦听器,因此将 input
状态添加到依赖项数组( 和任何linter 可能会抱怨其他缺少的依赖项 ),因此当 input
状态更新时,当前状态值会重新包含在回调范围中。
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]);