在哪里声明 getActiveUser 很重要。问题没有具体说明,但我假设您的组件看起来像这样:
const MyComponent = (props) => {
const getActiveUser() => {
//...
}
useEffect(() => {
getActiveUser();
}, []) // Lint error.
return <></>;
}
如果您的组件看起来像这样,您将不会收到 linter 错误:
const getActiveUser() => {
//...
}
const MyComponent = (props) => {
useEffect(() => {
getActiveUser();
}, []) // No error
return <></>;
}
那么为什么第一个是 linter 错误而第二个不是呢? linter 规则的重点是避免由于过时的 props 或 state 引起的问题。虽然getActiveUser 本身不是道具或状态,但当它在组件内部定义时,它可能依赖于可能是陈旧的道具或状态。
考虑这段代码:
const MyComponent = ({userId}) => {
const [userData, setUserData] = useState(null);
const getActiveUser() => {
setUserData(getData(userId)); // More realistically this would be async
}
useEffect(() => {
getActiveUser();
}, []);
//...
}
尽管 useEffect 依赖于 userId 属性,但它只运行一次,因此如果 userId 更改,userId 和 userData 将不同步。也许这是您的意图,但就 linter 规则而言,它看起来像一个错误。
在getActiveUser 定义在组件外部的情况下,它不可能(或至少不合理地)依赖于组件的状态或道具,因此 linter 规则没有问题。
那么如何解决这个问题?好吧,如果getActiveUser 不需要在组件内部定义,只需将其移出组件即可。
或者,如果您确定只希望在组件挂载时运行此行为,并且不会因道具更改而导致问题(最好假设所有道具都可以更改),那么您可以禁用linter 规则。
但假设这些都不是这种情况......
无解(效果太多)
正如您所指出的,将 getActiveUser 添加到 linter 数组可以解决问题:
const MyComponent = ({userId}) => {
const getActiveUser() => {
//...
}
useEffect(() => {
getActiveUser();
}, [getActiveUser]) // No error... but probably not right.
return <></>;
}
但是getActiveUser每次渲染都是不同的函数实例,所以就useEffect而言,deps数组每次渲染都会改变,这会导致每次渲染后调用API,这几乎肯定不是你想要的.
一个脆弱的解决方案
由于我示例中的根本问题是 userId 属性可能会更改,因此您还可以通过将 userId 添加到 useEffect 依赖项来解决此问题:
const MyComponent = ({userId}) => {
const getActiveUser() => {
// Uses userId
}
useEffect(() => {
getActiveUser();
// Linter is still unhappy, so:
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [userId])
return <></>;
}
这行为正确 - 没有额外的 API 调用或陈旧的数据 - 但 linter 仍然不开心:知道我们已经通过依赖 @987654343 的所有东西修复了对 getActiveUser 的依赖是不够聪明的@ 取决于。
这很脆弱:如果您在将来添加 getActiveUser 所依赖的 prop 或 state,而忘记在此处添加它,您将遇到过时数据问题。
更好的解决方案
所以推荐的解决方案是:
const MyComponent = ({userId}) => {
const getActiveUsers = useCallback(() => {
// uses userId
}, [userId]);
useEffect(() => {
getActiveUser();
}, [getActiveUsers]) // No error
return <></>;
}
通过将getActiveUsers 包装在useCallback 中,函数实例仅在需要时被替换:当userId 发生变化时。这意味着useEffect 也仅在需要时运行:当getActiveUsers 更改时(即userId 更改时)。
linter 对这个解决方案很满意,如果您向 getActiveUser 引入新的依赖项,您只需更改其 useCallback deps,而不是 useEffect。
Dan Abramov 的博文 A Complete Guide to useEffect 对此进行了更详细的介绍。