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

时间:2019-07-19 10:56:10

标签: javascript reactjs eslint react-hooks

根据官方的React文档,componentDidMount在钩子中翻译为:

useEffect(() => {
 //code here 
},[])

因此,假设我想在此挂钩中进行api调用:

useEffect(() => {
 getActiveUser(); 
},[])

添加eslint规则"react-hooks/exhaustive-deps"之后,这是一个皮棉错误。为了使它静音,我可以将getActiveUser函数放在数组中,然后一切正常。

但这是否违反文档?我的印象是该数组检查道具更改。我还要指出,API调用是在没有prop / id的情况下进行的,因此我可以理解必须执行以下操作的事实:

useEffect(() => {
 getActiveUser(someId); 
},[getActiveUser, someId])

那么这是怎么回事?添加Eslint规则意味着效果内的数组不能再次为空?

1 个答案:

答案 0 :(得分:3)

声明getActiveUser的位置很重要。该问题未指定,但我认为您的组件看起来像这样:

const MyComponent = (props) => {
    const getActiveUser() => {
       //...
    }
    useEffect(() => {
        getActiveUser();

    }, []) // Lint error.
    return <></>;
}

如果相反,您的组件看起来像这样,则不会出现短绒毛错误:

const getActiveUser() => {
    //...
}
const MyComponent = (props) => {
    useEffect(() => {
        getActiveUser(); 

    }, []) // No error
    return <></>;
}

那么为什么第一个是linter错误,而第二个不是?短绒规则的重点是要避免由于过时的道具或状态而引起的问题。尽管getActiveUser本身不是道具或状态,但在组件内部定义时,它可能取决于道具或状态,这可能是陈旧的。

考虑以下代码:

const MyComponent  = ({userId}) => {
    const [userData, setUserData] = useState(null);
    const getActiveUser() => {
        setUserData(getData(userId)); // More realistically this would be async
    }
    useEffect(() => {
        getActiveUser();
    }, []);

    //...
}

即使useEffect依赖于userId道具,它也只能运行一次,因此,{{1}和userId将在{{ 1}}的更改。也许这是您的意图,但就短绒规则而言,它看起来像是个错误。

userData是在组件外部定义的情况下,它不可能(或至少不是合理地)依赖于组件的状态或属性,因此linter规则没有问题。 / p>


那么如何解决这个问题?好吧,如果不需要在组件内部定义userId,只需将其移出组件即可。

或者,如果您确定只希望在组件安装时运行此行为,并且不会因道具更改而引起问题(最好假设所有道具都可以更改),则只需禁用短绒规则。

但是假设都不是这样...

非解决方案(影响太多)

如前所述,在棉绒阵列中添加getActiveUser可以解决问题:

getActiveUser

但是getActiveUser是每个渲染的不同函数实例,因此就const MyComponent = ({userId}) => { const getActiveUser() => { //... } useEffect(() => { getActiveUser(); }, [getActiveUser]) // No error... but probably not right. return <></>; } 而言,deps数组会更改每个渲染,这将在每次渲染后引起API调用,这几乎可以肯定不是你想要的。

一个脆弱的解决方案

由于在我的示例中,根本问题是getActiveUser属性可能会发生变化,因此您还可以通过在useEffect依赖项中添加userId来解决此问题:

userId

这是正确的行为-没有额外的API调用或过时的数据-但是linter仍然不满意:知道我们已经通过依赖于所有事物来解决对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 <></>; } 取决于。

这很脆弱:如果您将来添加getActiveUser所依赖的道具或状态,而忘记在此处添加它,那么您将遇到过时的数据问题。

更好的解决方案

所以推荐的解决方案是:

getActiveUser

通过将getActiveUser包装在const MyComponent = ({userId}) => { const getActiveUsers = useCallback(() => { // uses userId }, [userId]); useEffect(() => { getActiveUser(); }, [getActiveUsers]) // No error return <></>; } 中,仅在需要时替换函数实例:getActiveUsers发生变化时。这意味着useCallback也仅在需要时运行:userId发生更改时(即useEffect发生更改时)。

短毛绒对此解决方案感到满意,如果您将新的依赖项引入getActiveUsers,则只需更改其userId部门,而无需更改getActiveUser


Dan Abramov的博客文章A Complete Guide to useEffect对此进行了更详细的介绍。