让我们假设一个组件:
const Foo = ({id, onError}) => {
useEffect(() => {
subscribe(id).catch(error => onError(error));
return () => cleanup(id);
}, [id, onError]);
return <div>...</div>;
}
这个想法很简单——运行一个使用当前“id”订阅的效果。如果订阅失败,则调用作为 prop 传递的事件处理程序 onError
。
但是,要使其正常工作,传递的 onError
属性必须是引用稳定的。换句话说,如果我的组件的使用者尝试了以下操作,他们可能会遇到每次渲染都运行效果的问题:
const Parent = () => {
// handleError is recreated for each render, causing the effect to run each time
const handleError = error => {
console.log("error", error);
}
return <Foo id="test" onError={handleError} />
}
相反,他们需要执行以下操作:
const Parent = () => {
// handleError identity is stable
const handleError = useCallback(error => {
console.log("error", error);
},[]);
return <Foo id="test" onError={handleError} />
}
这有效,但我对它不满意。 Foo
的使用者需要意识到 onError
必须是稳定的,除非您查看其底层实现,否则这不是显而易见的。它打破了组件封装,消费者很容易在不知不觉中遇到问题。
是否有更好的模式来管理可能在 useEffect
内调用的事件处理程序之类的道具?
答案 0 :(得分:2)
您需要从您的依赖项列表中删除 onError
,但如果它发生变化仍会调用。为此,您可以使用 ref,并在每次渲染时通过 useEffect
更新它。
如果函数是 undefined
,您也可以使用 optional chaining ?.
来避免调用该函数。
const Foo = ({ id, onError }) => {
const onErrorRef = useRef();
useEffect(() => {
onErrorRef.current = onError;
});
useEffect(() => {
subscribe(id).catch(error => onErrorRef.current?.(error));
return () => cleanup(id);
}, [id]);
return <div>...</div>;
}