【发布时间】: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(()=>counter++)“生成”呼叫站点的唯一ID。它还会在增量后打印出counter的值。 - 将
id打印到控制台后返回生成的ID
Child 组件执行以下操作:
- 它调用
const id = useFoo() - 它返回一个按钮,单击该按钮将打印出
id和counter的值
不点击任何东西,useFoo 内的console.log 都会按预期执行,并将以下内容打印到控制台:
indide useState, counter: 1
inside useFoo, id: 1
现在,当您按下按钮时,您会认为 id 内的 Child 是 1,对吧?错了。
这是单击按钮时的输出:
id: 2 counter: 2
Here's 指向演示此行为的代码框的链接。
为什么会这样?
特别是,counter 如何在不执行 (1) 和 (2) 的情况下递增?
谢谢!
【问题讨论】:
-
确实是奇怪的行为,看起来
useState钩子以某种方式通过引用而不是值来跟踪counter值。 -
@Arvid 是的,类似的事情 - 尽管即使是这种情况,但您希望它仅在执行
counter++时才会增加,这应该仅在useState中的回调时发生被执行——这反过来会导致控制台上出现一条消息,但是没有,这使得这很奇怪 -
@Arvid 我收回了这一点,将
log更改为调试确实会产生一条消息。 -
我找到了一种方法来获得
x的“一致”值,方法是将console.log封装在useEffect中。虽然还没有解释这种行为。
标签: reactjs react-hooks closures