简体   繁体   English

如何在React中使用钩子实现componentDidMount以使其符合EsLint规则“ react-hooks / exhaustive-deps”:“ warn”?

[英]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更改,则userIduserData将不同步。 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... 但是假设这两种情况都不是...

A non-solution (too many effects) 非解决方案(影响太多)

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调用,这几乎肯定不是您想要的。

A fragile solution 脆弱的解决方案

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道具可能会改变,你也可以通过添加解决这个问题, userIduseEffect依赖关系:

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依赖的道具或状态,而忘了在此处添加它,那么您将遇到过时的数据问题。

A better solution 更好的解决方案

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.

相关问题 React Hooks react-hooks/exhaustive-deps eslint 规则似乎过于敏感 - React Hooks react-hooks/exhaustive-deps eslint rule seems overly sensitive 未找到规则“re​​act-hooks/exhaustive-deps”的定义 - Definition for rule 'react-hooks/exhaustive-deps' was not found React挂钩:如何使用react-hooks / exhaustive-deps规则在没有无限循环的情况下读取和更新挂钩中的状态 - React hooks: How to read & update state in hooks without infinite loops with react-hooks/exhaustive-deps rule 反应 useEffect 带有警告的钩子 react-hooks/exhaustive-deps - React useEffect hook with warning react-hooks/exhaustive-deps useEffect 和 &#39;react-hooks/exhaustive-deps&#39; linting - useEffect and 'react-hooks/exhaustive-deps' linting 带有自定义 IntersectionObserver 钩子的 react-hooks/exhaustive-deps 警告 - react-hooks/exhaustive-deps warning with custom IntersectionObserver hook ESLint 希望 setSate 作为 useEffect 的依赖,但这会导致无限循环(react-hooks/exhaustive-deps) - ESLint wants setSate as a dependency for useEffect but this causes an infinite loop (react-hooks/exhaustive-deps) 我如何正确使用 useEffect 进行异步获取调用和反应? 反应钩子/详尽的deps - How do i properly use useEffect for a async fetch call with react? react-hooks/exhaustive-deps 如何解决`react-hooks/exhaustive-deps`的`React Hook useEffect缺少依赖`? - How to solve `React Hook useEffect has a missing dependency` of `react-hooks/exhaustive-deps`? 设计React Hooks可以防止React-Hooks / Exhaustive-deps警告 - Designing React Hooks prevent react-hooks/exhaustive-deps warning
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM