setState函数在通过hook传递时是否应该是useEffect的依赖项?



所以,我偶然发现了这个奇怪的情况:

我有一个全局的React Context提供程序,提供一个全局的状态,像这样

const Context = createContext();
const ContextProvider = ({children}) => {
const [state, setState] = useState('');
return <Context.Provider value={{state, setState}}>{children}</Context.Provider>
}
const useMyState = () => {
const {state, setState} = useContext(Context);
return {
state,
setState
}
}
const Component = () => {
const {setState} = useMyState();
useEffect(() => {
elementRef.addEventListener('click', () => {
setState('someState');
});
return () => {
elementRef.removeEventListener('click', () => null); 
}
},[])
return <>
// ...
</>
}

eslint建议我的setState应该添加到useEffect的依赖数组中,

useEffect(() => {
elementRef.addEventListener('click', () => {
setState('someState');
});
},[setState])

我猜这可能在某种程度上与useMyState.ts文件内上下文的解构有关

但是感觉有点奇怪和不直观…

我的问题是setState是否真的需要在依赖数组内?如果是,为什么?

我的问题是setState是否真的需要在依赖数组内?

不,它不是,但是ESLint不知道,因为它没有办法知道你正在使用的上下文对象的setState成员是稳定的。你我知道这一点(因为setter被useState保证是稳定的,而且你是通过context和useMyState钩子逐字传递它的),但是ESLint不知道这一点。

你可以把它添加为一个依赖项来让ESLint高兴(如果你已经提供了一个数组,它不会有任何区别,因为setter永远不会改变;如果你的不是提供一个数组,见下文),或者你可以在注释中告诉ESLint跳过检查代码,或者你可以关闭规则(但它是非常)。很容易忽略依赖项,所以如果这样做的话要小心)。

(如果你不提供一个数组[因为你想让效果在每次渲染后运行],添加一个带有setter的数组将阻止这种情况发生,所以你会想要在这种情况下禁用ESLint错误选项。)或者有一些棘手的解决方案,比如使用一个不断增加数值的ref。: -)


这段代码有一个问题。它不断地向元素添加新的事件监听器,而不删除它们,因为使用时没有useEffect回调在每次组件渲染时被调用,并且你每次都创建一个新的事件处理函数,所以它们会叠加起来。

所以你需要让elementRef.current成为一个依赖,你需要一个清理回调:

const Component = () => {
const {setState} = useMyState();

useEffect(() => {
const handler = () => {
setState("someState");
};
const element = elementRef.current;
// Note −−−−−−−−−−−−−−−−−−^^^^^^^^
element.addEventListener("click", handler);
return () => {
element.removeEventListener("click", handler);
};
}, [elementRef.current]); // <== Optionally add `setState` to this
return <>
// ...
</>;
};

最新更新