Kle*_*leo 11 javascript reactjs eslint react-hooks
根据官方 React 文档,componentDidMount在 hooks 中翻译为:
useEffect(() => {
//code here
},[])
Run Code Online (Sandbox Code Playgroud)
所以假设我想在这个钩子中做一个 api 调用:
useEffect(() => {
getActiveUser();
},[])
Run Code Online (Sandbox Code Playgroud)
添加 eslint 规则后"react-hooks/exhaustive-deps",这是一个 lint 错误。为了使其静音,我可以将getActiveUser函数放入数组中,一切正常。
但这是否与文档背道而驰?我的印象是数组检查道具更改。我还想指出 API 调用是在没有 prop/id 的情况下进行的,所以我可以理解必须做这样的事情的事实:
useEffect(() => {
getActiveUser(someId);
},[getActiveUser, someId])
Run Code Online (Sandbox Code Playgroud)
那么这里发生了什么?加了Eslint规则,是不是Effect里面的数组不能再为空了?
Ret*_*sam 12
重要的getActiveUser是在哪里声明。问题没有具体说明,但我假设您的组件如下所示:
const MyComponent = (props) => {
const getActiveUser() => {
//...
}
useEffect(() => {
getActiveUser();
}, []) // Lint error.
return <></>;
}
Run Code Online (Sandbox Code Playgroud)
相反,如果您的组件看起来像这样,则不会出现 linter 错误:
const getActiveUser() => {
//...
}
const MyComponent = (props) => {
useEffect(() => {
getActiveUser();
}, []) // No error
return <></>;
}
Run Code Online (Sandbox Code Playgroud)
那么为什么第一个是 linter 错误而第二个不是呢?linter 规则的重点是避免由于过时的 props 或 state 导致问题。虽然getActiveUser它本身不是一个 prop 或 state,但当它在组件内部定义时,它可能依赖于 props 或 state,这可能是陈旧的。
考虑这个代码:
const MyComponent = ({userId}) => {
const [userData, setUserData] = useState(null);
const getActiveUser() => {
setUserData(getData(userId)); // More realistically this would be async
}
useEffect(() => {
getActiveUser();
}, []);
//...
}
Run Code Online (Sandbox Code Playgroud)
尽管这useEffect取决于userId道具,但它只运行一次,因此如果更改, theuserId和 theuserData将不同步userId。也许这是您的意图,但就 linter 规则而言,它看起来像是一个错误。
在 wheregetActiveUser在组件外部定义的情况下,它不可能(或至少不合理地)依赖于组件的状态或道具,因此 linter 规则没有问题。
那么如何解决这个问题呢?好吧,如果getActiveUser不需要在组件内部定义,只需将其移出组件即可。
或者,如果您确定只希望在组件安装时运行此行为,并且不会因道具更改而导致问题(最好假设所有道具都可以更改),那么您可以禁用 linter 规则。
但假设这两种情况都不是......
正如您所指出的,添加getActiveUser到 linter 数组会使问题消失:
const MyComponent = ({userId}) => {
const getActiveUser() => {
//...
}
useEffect(() => {
getActiveUser();
}, [getActiveUser]) // No error... but probably not right.
return <></>;
}
Run Code Online (Sandbox Code Playgroud)
但是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 <></>;
}
Run Code Online (Sandbox Code Playgroud)
这行为正确 - 没有额外的 API 调用或过时的数据 - 但 linter 仍然不满意:它不够聪明,知道我们已经getActiveUser通过依赖所有getActiveUser依赖的东西来修复依赖。
这是脆弱的:如果您在未来getActiveUser添加依赖的道具或状态,而忘记在此处添加它,您将遇到过时的数据问题。
所以推荐的解决方案是:
const MyComponent = ({userId}) => {
const getActiveUsers = useCallback(() => {
// uses userId
}, [userId]);
useEffect(() => {
getActiveUser();
}, [getActiveUsers]) // No error
return <></>;
}
Run Code Online (Sandbox Code Playgroud)
通过包装getActiveUsers在 中useCallback,函数实例仅在需要时才被替换:当userId更改时。这意味着useEffect也仅在需要时运行:当getActiveUsers更改时(即每当userId更改时)。
linter 对这个解决方案很满意,如果你向 引入新的依赖项getActiveUser,你只需要改变它的useCallbackdeps,而不是useEffect.
Dan Abramov 的博文A Complete GuideuseEffect对此进行了更详细的介绍。
| 归档时间: |
|
| 查看次数: |
2022 次 |
| 最近记录: |