在React + TypeScript中,我试图创建一个自定义钩子,它返回具有引用类型和稳定签名的稳定更新器函数,例如来自useState或useDispatch的那些证明,因此eslint规则hooks/exhaustive在钩子的依赖项数组中不需要这样做-deps
这是一个例子:
import { useRef, useState, useEffect, useCallback } from 'react';
export type SetStateWithCallback<T> = React.SetStateAction<T> | ((newState: React.SetStateAction<T>, callback?: React.RefObject<Function>) => T);
export function useStateWithCallback<T>(initState: T): [T, React.Dispatch<SetStateWithCallback<T>>] {
const [state, setState] = useState<T>(initState);
const callbackRef = useRef<Function>(null);
const setStateWithCallback = useCallback((newState: React.SetStateAction<T>, callback?: React.RefObject<Function>) => {
callbackRef.current = typeof callback === 'function' ? callback : null;
setState(newState);
}, []);
useEffect(() => {
if (callbackRef.current) {
callbackRef.current?.(state);
callbackRef.current = null;
}
}, [state]);
return [state, setStateWithCallback];
}
但是当使用这个自定义钩子时,eslint 规则 hooks/exhaustive-deps 仍然要求将
setStateWithCallback
作为依赖项包含在内,这与它对内置 setState
所做的不同,例如:
function MyTestComponent() {
const [state, setState] = useState({});
const [customState, setCustomState] = useStateWithCallback({});
const test = useCallback(() => setState({}), []); // Fine. No required dependency
const testCustom = useCallback(() => setCustomState({}), []); // Warning: React Hook useCallback has a missing dependency: 'setCustomState'. Either include it or remove the dependency array. eslint(hooks/exhaustive-deps)
return null;
}
如何更改
setCustomState
才能实现与 setState
相同的效果?
This is a design choice in React. It's not practical to force custom functions to behave the same way as built-in hooks in terms of dependency handling.
React对其内置钩子的运行方式有着深入的内部了解,允许React团队优化这些钩子以防止不必要的重新渲染或重新执行。此优化是针对这些钩子的常见使用模式量身定制的。
但是,自定义函数是不同的。它们可以在每次渲染时重新定义,尤其是在组件内声明它们时。这种重新定义可能会发生,因为自定义函数通常会关闭组件的状态或属性。当一个函数被重新定义时,它的身份就会改变。如果您从 useEffect 或 useCallback 等挂钩的依赖项列表中省略此类函数,React 可能不会在必要时重新运行效果或重新计算值,从而导致潜在的错误或不一致的行为。在依赖项数组中包含自定义函数可确保每当函数或其依赖数据发生更改时效果或记忆值都能正确更新。