我有一个具有某些内部状态的组件,该状态仅在某些事件上不会在整个应用程序中传播。
const FunComponent = (initialDescription, onSave) => {
const [description, setDescription] = useState('initialDescription');
const handleDescriptionSave = () => {
onSave(description);
}
return ( ... );
}
我的问题是组件可以被组件树中更高的东西卸载,在那一点上,我想强制触发onSave
。太好了——我想——我可以用一个空的依赖数组写一个useEffect,它将在卸载时运行。
问题是onSave
道具和description
状态都是我的useEffect清理所依赖的。
如果我将它们添加到依赖数组中,它将在每次描述更改时保存之前的描述值。我不想那样。如果我不将它们添加到依赖数组中,它将在卸载时只使用初始的onSave
和description
值。
const FunComponent = (initialDescription, onSave) => {
const [description, setDescription] = useState(initialDescription);
const handleDescriptionSave = () => {
onSave(description);
}
useEffect(() => {
return () => { onSave(description) };
}, [onSave, description])
return ( ... );
}
我想到的是将回调存储在ref中,并使其保持最新,并调用ref unmount:
const FunComponent = (initialDescription, onSave) => {
const [description, setDescription] = useState(initialDescription);
const handleDescriptionSave = useCallback(() => {
onSave(description);
}, [onSave, description]);
const handleDescriptionSaveRef = useRef(handleDescriptionSave);
useEffect(() => {
handleDescriptionSaveRef.current = handleDescriptionSave;
}, [onSave, description]);
useEffect(() => {
return () => { handleDescriptionSaveRef.current(); };
}, []);
return ( ... );
}
但这似乎是超级愚蠢的。
是否有一个我没有认识到的通用推荐模式?这会不会以某种方式失败?这是一个好的解决方案吗?
也许你可以直接将回调存储到ref中,而不需要useCallback()
:
const FunComponent = (initialDescription, onSave) => {
const [description, setDescription] = useState(initialDescription);
const handleDescriptionSaveRef = useRef();
useEffect(() => {
handleDescriptionSaveRef.current = () => onSave(description);
}, [onSave, description]);
useEffect(() => {
return () => { handleDescriptionSaveRef.current(); };
}, []);
return ( ... );
}
有趣的问题
你的方法看起来并不坏。您可以像这样使用特殊的钩子https://github.com/streamich/react-use/blob/master/src/useUnmount.ts
在我们的例子中,你可以为钩子提供两个参数:cb和args
const useUnmount = (fn: () => any, ...args): void => {
const fnRef = useRef(fn);
// update the ref each render so if it change the newest callback will be invoked
fnRef.current = () => fn(...args);
useEffectOnce(() => () => fnRef.current());
};