当通过钩子传递时,setState 函数是否应该是 useEffect 的依赖项
Should a setState function be a dependency of useEffect when passed via hook
所以,我偶然发现了这种奇怪的情况:
我有一个全局 React 上下文提供程序,提供一个全局状态,就像这样
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
是否真的需要依赖数组?如果是,为什么?
my question is is the setState
really required inside the dependency array?
不,它不是,但 ESLint 不知道这一点,因为它无法知道您正在使用的上下文对象的 setState
成员是否稳定。 你知道(因为 setter 被 useState
保证稳定,你通过上下文和你的 useMyState
钩子逐字传递它), 但 ESLint 不知道。
您可以将其添加为依赖项以使 ESLint 满意(如果您已经提供数组,它不会有任何区别,因为 setter 永远不会改变;如果您 not 提供数组),或者你可以在注释中告诉 ESLint 跳过检查该代码,或者你可以关闭规则(但它 very 容易错过依赖项,所以如果你这样做要小心)。
(如果您没有提供数组 [因为您希望在每次渲染后效果为 运行],添加一个包含 setter 的数组将阻止这种情况发生,所以在这种情况下,您需要选择禁用 ESLint 错误。或者有一些棘手的解决方案,例如使用其中包含 ever-increasing 数值的 ref。:-) )
不过,该代码存在问题。它反复向元素添加新的事件侦听器而不删除它们,因为使用 no 依赖数组,每次组件呈现时都会调用 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 <>
// ...
</>;
};
所以,我偶然发现了这种奇怪的情况:
我有一个全局 React 上下文提供程序,提供一个全局状态,就像这样
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
是否真的需要依赖数组?如果是,为什么?
my question is is the
setState
really required inside the dependency array?
不,它不是,但 ESLint 不知道这一点,因为它无法知道您正在使用的上下文对象的 setState
成员是否稳定。 你知道(因为 setter 被 useState
保证稳定,你通过上下文和你的 useMyState
钩子逐字传递它), 但 ESLint 不知道。
您可以将其添加为依赖项以使 ESLint 满意(如果您已经提供数组,它不会有任何区别,因为 setter 永远不会改变;如果您 not 提供数组),或者你可以在注释中告诉 ESLint 跳过检查该代码,或者你可以关闭规则(但它 very 容易错过依赖项,所以如果你这样做要小心)。
(如果您没有提供数组 [因为您希望在每次渲染后效果为 运行],添加一个包含 setter 的数组将阻止这种情况发生,所以在这种情况下,您需要选择禁用 ESLint 错误。或者有一些棘手的解决方案,例如使用其中包含 ever-increasing 数值的 ref。:-) )
不过,该代码存在问题。它反复向元素添加新的事件侦听器而不删除它们,因为使用 no 依赖数组,每次组件呈现时都会调用 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 <>
// ...
</>;
};