【问题标题】:useState hook mysteriously re-executed without a traceuseState 钩子神秘地重新执行而无影无踪
【发布时间】:2021-11-02 15:44:33
【问题描述】:

这个问题可能与钩子无关,而是与闭包有关,但我完全不知道它是如何工作的。

我正在编写一个自定义钩子,并试图为组件中的每个调用点获取一个唯一的 ID。为此,我使用useState(() => counter++),其中counter 每个组件“实例化”一次。

或者这就是理论。实际代码做了一些我一生都无法理解的事情。

以下是此行为的最小可验证示例:

const { useState } = require("react");

let counter = 0;
const useFoo = () => {
  const [id] = useState(() => {
    counter++;
    console.log("indide useState, counter:", counter); // (1)
    return counter;
  });
  console.log("inside useFoo, id:", id); // (2)
  return id;
};

export const Child = () => {
  const id = useFoo();
  return (
    <button onClick={() => console.log("id:", id, "counter:", counter)}>
      Click me!
    </button>
  );
};

useFoo 挂钩执行以下操作:

  • 它使用useState(()=&gt;counter++)“生成”呼叫站点的唯一ID。它还会在增量后打印出counter 的值。
  • id打印到控制台后返回生成的ID

Child 组件执行以下操作:

  • 它调用const id = useFoo()
  • 它返回一个按钮,单击该按钮将打印出idcounter 的值

不点击任何东西,useFoo 内的console.log 都会按预期执行,并将以下内容打印到控制台:

indide useState, counter: 1
inside useFoo, id: 1

现在,当您按下按钮时,您会认为 id 内的 Child1,对吧?错了。

这是单击按钮时的输出:

id: 2 counter: 2

Here's 指向演示此行为的代码框的链接。

为什么会这样?

特别是,counter 如何在不执行 (1)(2) 的情况下递增?

谢谢!

【问题讨论】:

  • 确实是奇怪的行为,看起来useState 钩子以某种方式通过引用而不是值来跟踪counter 值。
  • @Arvid 是的,类似的事情 - 尽管即使是这种情况,但您希望它仅在执行 counter++ 时才会增加,这应该仅在 useState 中的回调时发生被执行——这反过来会导致控制台上出现一条消息,但是没有,这使得这很奇怪
  • @Arvid 我收回了这一点,将log 更改为调试确实会产生一条消息。
  • 我找到了一种方法来获得x 的“一致”值,方法是将console.log 封装在useEffect 中。虽然还没有解释这种行为。

标签: reactjs react-hooks closures


【解决方案1】:

有副作用的代码必须放入useEffect。这样做的原因是不是仅仅因为直接作为函数体的一部分编写的代码可以在任意时刻执行 - 但它甚至可能是stateuseEffect 之外的情况可能看起来不一致。

我不知道如何创建“增量”id,但是通过将所有副作用相关代码放入useEffect,您至少可以对useState 返回的值有一个一致的看法,并且useState 返回的值绝对是唯一的。这是一个例子:

const { useState, useEffect } = require("react");

let counter = 0;
const useFoo = () => {
  const [id] = useState(() => {
    counter++;
    return counter;
  });

  useEffect(() => console.log("useState returned:", id), []);
  return id;
};

export const Child = () => {
  const id = useFoo();
  return <button onClick={() => console.log("id:", id)}>Click me!</button>;
};

这将输出

useState returned: 2
id: 2

useFoo 的函数体应该是纯的; react 只保证useEffect 中的函数以及最终渲染的组件对组件的状态有一致的看法。

【讨论】:

  • 很高兴你能弄明白。 Hooks 在后台有很多魔法,所以要为更多这样的惊喜做好准备 :) 但幸运的是,大多数时候,它只是按预期工作。
猜你喜欢
  • 1970-01-01
  • 2021-06-06
  • 1970-01-01
  • 1970-01-01
  • 2021-10-05
  • 2017-05-15
  • 2020-12-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多