【问题标题】:Are all variables we use in useEffect in dependencies array?我们在 useEffect 中使用的所有变量都在依赖项数组中吗?
【发布时间】:2019-05-21 02:54:27
【问题描述】:

我知道 useEffect 的依赖项是如何工作的。但是,在我的场景中,我不应该看到 prop 值的变化,我将其用作处理流程的条件,但如果我不将其放入依赖项数组中,我将收到 react-hooks/exhaustive-deps 警告。

我的情况是,如果 foo 和 fetchValue 发生变化,我想重新运行整个 fetch。虽然我使用 bar 作为条件来决定调用的是 fechValue,但在我的业务逻辑中,bar 的更改不应该重新运行块。

const Component = ({ foo, bar, fetchValue }) => {
  useEffect = (
    () => {
      if(foo) {
        if (bar) {
          fetchValue();
        }
      }
    },
    [foo, fetchValue] // will get a warning `react-hooks/exhaustive-deps`
  )

  return <div />

}

【问题讨论】:

  • 这听起来不像是一个有效的商业案例,更像是要求将来很难调试竞争条件 - 请您详细说明为什么需要这样做?

标签: reactjs react-hooks


【解决方案1】:

ESLint 规则用于通过 useEffect 挂钩提供安全性。

如果您绝对确定该值不是 useEffect 的依赖项,则可以添加注释以忽略 ESLint 规则:Disabling Rules with Inline Comments

我建议添加// eslint-disable-next-line 而不是文件范围的eslint-disable

这里是more context about this by Dan Abramov

有什么方法可以专门针对使用扩展运算符的地方禁用此规则?

如果您认为自己知道自己在做什么,可以随时// eslint-disable-next-line react-hooks/exhaustive-deps

【讨论】:

  • 当然。我知道如何禁用警告。我只想讨论一些很好的工具来避免警告并以正确的方式进行。
  • 该工具可以捕捉常见用例的错误,例如useEffect 内部使用的外部变量,它进行了一些优化以防止每次重新渲染时调用。您所描述的是一种极端情况,该工具不支持,因此您应该知道自己在做什么并禁用该规则。我提供了 Dan Abramov 的评论,准确地解释了这一点。
【解决方案2】:

您应该将您使用的所有变量都放在 useEffects 'deps' 数组中的原因是不这样​​做可能会由于数据陈旧而产生奇怪的问题。


例如,给定您的用例,假设您的组件 props 最初是 {foo: false, bar: false }(假设 fetchValue 是某个固定函数),然后它们更改为 {foo: true, bar: true}。在这种情况下,您的组件将按预期调用fetchValue

但作为另一个例子,假设你的道具从{foo: false, bar: false} 更改为{foo: true, bar: false},然后更改为{foo: true, bar: true}。在这种情况下,fetchValue 没有触发,尽管道具与上一个示例末尾的相同。

也许这本身并没有“错误”,但它肯定很奇怪且不直观:理想情况下,您的组件应该根据其 props 一致地运行,并且 props 更改的顺序无关紧要。


所以,是的,您始终可以eslint-ignore deps 数组以使其不完整,但我建议个人寻找另一种解决方案。

如果没有关于用例的更多信息,很难具体说明,但也许可以记住对fetchValue 的调用,这样如果foo 没有改变,它就不会做任何事情?或者 foobar 可以组合成一个单独的道具,这样它们就可以一起改变?

【讨论】:

  • “假设你的道具从 {foo: false, bar: false} 变为 {foo: true, bar: false}”。它会在那里开火,据我了解,这就是@WendellLiu 想要的。仅此而已,因为“然后更改为{foo:true,bar:true}”不需要触发它。
  • @Robbeoli useEffect 会在那里触发,但 fetchValue 不会被触发,因为它被包裹在 if(bar) 中,而 bar 仍然是 false
  • 哦,是的,没错,对我来说,这种用法确实让人觉得很直观。我将bar 视为某种状态,用于确定fetchValue 是否可以,foo 视为需要为真的实际触发器,并检查条形状态是否为真。
  • @Retsam 如果bar 是这个Component 中的一个状态怎么办?我是否需要将其添加到依赖数组中以获取 useEffect 中的最新值?
  • @NiyasNazar 是的 - 您不需要包含在 deps 数组中的唯一内容是在组件外部定义的内容(例如导入或模块级变量)以及 React 保证为常量的内容(状态设置器、调度函数和引用对象)。
【解决方案3】:

类似的逻辑经常发生在“useEffect”中的 if 语句中。首先,根本不需要添加依赖项,您可以将其留空。

现在您可以做一些事情,或者简单地忽略警告,因为您的代码看起来不错。或者重构您的代码,其中barfetchValue() 的参数。所以你的代码是:

const Component = ({ foo, bar, fetchValue }) => {
  useEffect = (
    () => {
      if(foo) {
        fetchValue(bar);        
      }
    },
    [foo, fetchValue] 
  )

  return <div />

}

并以if(baz){ //your code} 开始您的fetchValue(baz) 声明。

虽然有点麻烦。

您需要问自己,您期望 foo 或 bar 更改的原因和位置是什么?在组件本身内部? foo 和/或bar 不应该是状态然后foo 和或bar 作为默认值?

const Component = ({ foo, bar, fetchValue }) => {

  const [fooState, setFooState] = useState(foo);
  const [barState, setBarState] = useState(bar);

  useEffect = (
    () => {
      fooState && barState && fetchValue(); 
    //does the same as your code, I just prefer short circuits for these kind of things.
    },
    [fooState, fetchValue()] 
  )

  return <div />

}

【讨论】:

  • 重构fetchValue 并不能解决任何问题,因为代码仍在使用useEffect 中的bar 值。我支持第二部分,如果 useEffect 中的值无关紧要,代码可能会被重构以删除它在 useEffect 中的用法。
  • 您的两个示例仍然会引发相同的 linter 警告,并且将 props 移入状态通常不是一个好主意。 (这意味着组件将完全忽略对这些道具的更改,这通常是不希望的)
  • 也许我当时做了一些奇怪的事情,因为当我在一个类似的用例上偶然发现这个警告时,后来不得不将我的道具更改为自定义延迟的状态,警告消失了:@987654336 @这里没有警告。
  • @Retsam 你提出了一个奇怪的论点。你说改变的道具不会影响useEffect,但不知何故这会是状态的问题?这是非常假设的,您能否提供一个更具体的示例以供我们扩展?
  • @MarkoGrešak 第二个代码 sn-p 是一个具体的例子。通过在初始渲染时将foo 复制到fooStatefoo 的所有未来值都将被忽略:useState 仅读取其默认值一次。 (这是与useEffect 完全不同的问题)
猜你喜欢
  • 2021-02-11
  • 2021-08-20
  • 2021-09-07
  • 2020-01-05
  • 2022-09-30
  • 2020-06-09
  • 2020-07-22
  • 2022-06-12
  • 2021-05-19
相关资源
最近更新 更多