[英]How to implement componentDidMount with hooks in React to be in line with the EsLint rule “react-hooks/exhaustive-deps”: “warn”?
According to the official React documentation, componentDidMount
is translated in hooks as: 根据React的官方文档,
componentDidMount
在钩子中翻译为:
useEffect(() => {
//code here
},[])
So assuming I want to do an api call within this hook: 因此,假设我想在此挂钩中进行api调用:
useEffect(() => {
getActiveUser();
},[])
After adding the eslint rule "react-hooks/exhaustive-deps"
, this is a lint error. 添加eslint规则
"react-hooks/exhaustive-deps"
,这是一个皮棉错误。 In order to silence it I can just drop the getActiveUser
function inside the array and everything works just fine. 为了使它静音,我可以将
getActiveUser
函数放到数组中,然后一切正常。
But does that go against the documentation? 但这是否违背了文档? I was under the impression that the array checks for prop changes.
我的印象是该数组检查道具更改。 I would like also to point out that the API call is being made without a prop/id, so I could understand the fact of having to do something like that:
我还要指出,API调用是在没有prop / id的情况下进行的,因此我可以理解必须执行以下操作的事实:
useEffect(() => {
getActiveUser(someId);
},[getActiveUser, someId])
So what's going on here? 那么这是怎么回事? Adding the Eslint rule mean that the array inside the effect can't be empty again?
添加Eslint规则意味着效果内的数组不能再次为空?
It matters where getActiveUser
is declared. 声明
getActiveUser
位置很重要。 The question doesn't specify, but I assume your component looks something like this: 该问题未指定,但我假设您的组件看起来像这样:
const MyComponent = (props) => {
const getActiveUser() => {
//...
}
useEffect(() => {
getActiveUser();
}, []) // Lint error.
return <></>;
}
If instead your component looked like this, you wouldn't get a linter error: 相反,如果您的组件看起来像这样,则不会出现linter错误:
const getActiveUser() => {
//...
}
const MyComponent = (props) => {
useEffect(() => {
getActiveUser();
}, []) // No error
return <></>;
}
So why is the first a linter error and the second not? 那么,为什么第一个是lint错误而第二个不是? The point of the linter rule is to avoid issue due to stale props or state.
短绒规则的重点是要避免由于过时的道具或状态而引起的问题。 While
getActiveUser
is not itself a prop or state, when its defined inside the component, it may depend on props or state, which may be stale. 尽管
getActiveUser
本身不是属性或状态,但是在组件内部定义它时,它可能取决于属性或状态,这可能是陈旧的。
Consider this code: 考虑以下代码:
const MyComponent = ({userId}) => {
const [userData, setUserData] = useState(null);
const getActiveUser() => {
setUserData(getData(userId)); // More realistically this would be async
}
useEffect(() => {
getActiveUser();
}, []);
//...
}
Even though that useEffect
depends on the userId
prop, it only runs once, and so the userId
and the userData
will be out of sync if the userId
changes. 即使
useEffect
取决于userId
useEffect
,它也只能运行一次,因此,如果userId
更改,则userId
和userData
将不同步。 Maybe this is your intent, but for the purposes of the linter rule it looks like a bug. 也许这是您的意图,但就短绒规则而言,它看起来像是个错误。
In the case where getActiveUser
is defined outside the component, it can't possibly (or at least not reasonably) depend on the state or props of the component, so there's no issue for the linter rule. 如果
getActiveUser
是在组件外部定义的,则它不可能(或至少不是合理地)依赖于组件的状态或属性,因此linter规则没有问题。
So how to fix this? 那么如何解决呢? Well, if
getActiveUser
doesn't need to be defined inside the component, just move it out of the component. 好吧,如果不需要在组件内部定义
getActiveUser
,只需将其移出组件即可。
Alternatively, if you're sure you only want this behavior to run when the component mounts, and that won't cause issue due to props changing (it's best to assume all props can change), then you can just disable the linter rule. 另外,如果您确定只希望在挂载组件时运行此行为,并且不会因道具更改而引起问题(最好假设所有道具都可以更改),则可以禁用linter规则。
But assuming neither of those is the case... 但是假设这两种情况都不是...
As you've noted, adding getActiveUser
to the linter array makes the issue go away: 正如您已经提到的,将
getActiveUser
添加到linter数组使问题消除了:
const MyComponent = ({userId}) => {
const getActiveUser() => {
//...
}
useEffect(() => {
getActiveUser();
}, [getActiveUser]) // No error... but probably not right.
return <></>;
}
But getActiveUser
is a different function instance every render, so as far as useEffect
is concerned, the deps array changes every render, which will cause an API call after every render, which is almost certainly not what you want. 但是
getActiveUser
是每个渲染的不同函数实例,因此就useEffect
而言,deps数组会更改每个渲染,这将在每次渲染后引发API调用,这几乎肯定不是您想要的。
Since the root issue in my example is that the userId
prop might change, you could also fix this issue by adding userId
to the useEffect
dependencies: 因为在我的例子根本的问题是,
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 <></>;
}
This behaves correctly - no extra API calls or stale data - but the linter is still unhappy: it isn't clever enough to know that we've fixed the dependency on getActiveUser
by depending on all the things that getActiveUser
depends on. 这种行为正确-没有多余的API调用或过时数据-但棉短绒还是不高兴:这是不够聪明,知道我们固定的依赖
getActiveUser
通过取决于所有的事情getActiveUser
依赖。
And this is fragile: if you add a prop or state in the future that getActiveUser
depends on, and forget to add it here, you're going to have stale data issues. 这很脆弱:如果将来添加
getActiveUser
依赖的道具或状态,而忘了在此处添加它,那么您将遇到过时的数据问题。
So the recommended solution is: 因此,推荐的解决方案是:
const MyComponent = ({userId}) => {
const getActiveUsers = useCallback(() => {
// uses userId
}, [userId]);
useEffect(() => {
getActiveUser();
}, [getActiveUsers]) // No error
return <></>;
}
By wrapping getActiveUsers
in useCallback
, the function instance is only replaced when needed: when userId
changes. 通过将
getActiveUsers
包装在useCallback
,仅在需要时(当userId
更改时)才替换函数实例。 This means that the useEffect
also only runs when needed: when getActiveUsers
changes (which is whenever userId
changes). 这意味着
useEffect
也仅在需要时运行:当getActiveUsers
更改时(即userId
更改时)。
The linter is happy with this solution and if you introduce new dependencies to getActiveUser
, you'll only need to change its useCallback
deps, not the useEffect
. linter对此解决方案感到满意,如果您将新的依赖项引入
getActiveUser
,则只需更改其useCallback
deps,而无需更改useEffect
。
Dan Abramov's blogpost A Complete Guide to useEffect
goes into this in more detail. 丹·阿布拉莫夫(Dan Abramov)的博客文章《使用
useEffect
的完整指南》对此进行了更详细的介绍。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.