是“useEffect 缺少依赖项"吗?警告有时出错? [英] Is the "useEffect has a missing dependency" warning sometimes wrong?

查看:35
本文介绍了是“useEffect 缺少依赖项"吗?警告有时出错?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用钩子一段时间了,但我从来没有完全理解为什么 React 强迫我在 useEffect 中包含一些我不想要的依赖项.

I've been using hooks for a while and I never fully understand why React is forcing me to includes some dependencies that I don't want on my useEffect.

我理解 useEffect 钩子的依赖关系"的方式

添加您想要聆听"的值,只要它们发生变化并触发您的效果.这与一个简单的效果完美配合:

Add the values you want to 'listen' whenever they change and trigger your effect. This works perfectly with a simple effect like:

import React, {useEffect, useState} from "react";

interface Props {
    id: string
}

const SimpleComponent = (props: Props) => {
    const {id} = props;
    const [response, setResponse] = useState<object>();
    
    useEffect(() => {
        fetch(`https://myexample/${id}`)
            .then(response => setResponse(response))
            .catch(() => console.log("An error occurs!"))
    }, [id])
    
    return <div/>
};

然而,还有一些其他情况不像上面的例子那么简单.在这个例子中,我们只想在 id 改变时触发效果:

However, there are some other cases where is not as straightforward as the example above. In this example we want to trigger the effect only when the id changes:

import React, {useEffect} from "react";

interface Props {
    id: string
    callback: Function
}

const SimpleComponent = (props: Props) => {
    const {id, callback} = props;
    
    useEffect(() => {
        callback(id)
    }, [id]);

    return <div/>
};

在这个例子中,我收到警告React Hook useEffect has a missing dependency";它建议在依赖项数组中包含回调"(选项 1)或删除依赖项数组(选项 2).

In this example, I get the warning "React Hook useEffect has a missing dependency" it recommends to include 'callback' on the dependencies array (option 1) or remove the dependencies array (option 2).

让我们探索一下这些建议:

  • 选项 1(在依赖项数组中包含回调"):在依赖项数组中包含 'callback' 将导致我的效果在 'id' 或 'callback' 更改时触发.这样做的问题是,我不想在回调"更改导致回调在每次渲染中更改时触发效果.

  • Option 1 (Include 'callback' on the dependencies array): Including 'callback' on the dependencies array will cause my effect to trigger whenever the 'id' or the 'callback' changes. The problem with this is that I don't want to trigger the effect when the 'callback' changes cause the callback will change in every render.

选项 2(删除依赖项数组):移除依赖数组会导致我的效果在组件发生变化时触发,这也不是我们想要的行为.

Option 2 (remove the dependencies array): Removing the dependency array will cause my effect to trigger whenever the component changes which is not the wanted behavior either.

我从社区中找到了一些其他建议,但所有这些建议似乎都没有完成想要的行为.(https://stackoverflow.com/a/60327893/8168782)

I've found some other advice from the community but all of them seem to not accomplish the wanted behavior. (https://stackoverflow.com/a/60327893/8168782)

让我们快速回顾一下这些选项:

Let's quickly review these options:

选项 1:使用空的依赖项数组:

它只会在组件挂载时触发,而不是我们想要的.

It will only trigger when the component mounts, not what we want.

选项 2:在 useEffect() 内部声明函数

在这种情况下,'callback' 是一个通过 props 传递的函数,但无论哪种方式,大多数情况下你都不能在 effect 中声明该函数,因为该函数在其他地方使用.

In this case, the 'callback' is a function passed through props, but either way most of the time you can't declare the function inside the effect because the function is used in other places.

选项 3:使用 useCallback() 进行记忆

如果你将你的函数包装在 useCallback 中,你还需要将依赖项包含到 useCallback 依赖项数组中,这将导致每次依赖项更改时再次触发 useCallback,因此也会触发 useEffect.

If you wrap your function inside a useCallback you also need to include the dependencies into the useCallback dependencies array, this will cause the useCallback to trigger again every time the dependencies change so the useEffect also will be triggered.

选项 4:禁用 eslint 的警告

未考虑,因为我试图理解问题而不是简单地忽略它.

Not considered because I'm trying to understand the problem not simply ignore it.

我对这个警告真的很困惑,我不知道是否在某些情况下警告是错误的并且应该被忽略(似乎是错误的)或者我遗漏了什么.

I'm really confused about this warning, I don't know if there are some situations where the warning is wrong and should be ignored (what seems to be wrong) or that I'm missing something.

推荐答案

我个人总是禁用 eslint 规则.由于 eslint 无法理解你的逻辑内涵,它只能彻底检查闭包中捕获的所有变量,并警告你从 dep-list 中丢失的变量.但是很多时候它是矫枉过正的,就像在您的用例中一样.这就是我选择的理由.

I personally always disable that eslint rule. Due to that eslint has no way to understand your logical intension, it can only exhaustively check all variables captured in the closure and warn you about missing ones from dep-list. But a lot of time it's overkilling, just like in your use case. That's the reasoning behind my choice.

如果您清楚地了解 useEffect 的工作原理,禁用此规则不会造成太大的痛苦.我个人不记得经历过.

If you have a clear understanding of how useEffect works, disabling this rule wouldn't cause much pain. I personally don't remember experiencing it.

第二种解决方案是保留规则,但要绕过它.我为你准备了一个,useFn 自定义钩子:

A second solution is to leave the rule on, but work around it. I got one for you, the useFn custom hook:

function useFn(fn) {
  const ref = useRef(fn);
  ref.current = fn;

  function wrapper() {
    return ref.current.apply(this, arguments)
  }

  return useRef(wrapper).current
}

这个钩子返回一个wrapper函数的稳定引用,它只是一个调用实际fn的代理,但在重新渲染时不会改变.>

This hook returns a stable reference of wrapper function, which is just a proxy that call the actual fn, but doesn't change across re-rendering.

const SimpleComponent = (props: Props) => {
    const {id, callback: _callback} = props;

    const callback = useFn(_callback)
    
    useEffect(() => {
        callback(id)
    }, [id, callback]);

    return <div/>
};

现在你满足了 eslint 规则,同时你不会触发不需要的 useEffect 重新运行.

Now you satisfy that eslint rule meanwhile you don't trigger unwanted useEffect re-run.

作为题外话.我还使用 useFn 钩子来包装传递给子组件 props 的函数.

As an off-topic side note. I also use useFn hook to wrap functions that got passed to child components' props.

传递箭头函数是 React 中大量使用的模式.有时你有一个重新渲染的组件很昂贵,你 React.memo(Component) 包装它,然后你传递一个 { ... }}/> 内联函数,有效地使 memoize 效果失效.useFn 来拯救:

Passing arrow function around is heavily used pattern in React. Sometimes you have a component that's expensive to re-render, you React.memo(Component) wrap it, yet then you pass a <Component onClick={e => { ... }} /> inline function, which effectively invalidate the memoize effect. useFn comes to rescue:

<Component onClick={useFn(e => { ... })} />

这篇关于是“useEffect 缺少依赖项"吗?警告有时出错?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆