React的useState setter lambdas 运行 可以多次使用吗?
Can React's useState setter lambdas run multiple times?
老实说,除了“这里发生了什么?”之外,我正在努力想出一种表达这个问题的方法。采用以下 React 代码,旨在将增量项目添加到列表中:
import React, { useState } from "react";
import "./styles.css";
let counter = 0;
export default function App() {
const [list, setList] = useState([]);
console.info("Render:", counter, list.join());
return (
<div className="App">
{list.join()}
<button
onClick={() => {
setList((prevList) => {
console.info("Pre-push:", counter, prevList.join());
const newList = [...prevList, "X" + ++counter];
console.info("Post-push:", counter, newList.join());
return newList;
});
}}
>
Push
</button>
</div>
);
}
如果您 运行 使用 https://codesandbox.io/s/amazing-sea-6ww68?file=/src/App.js 的代码并单击“推送”按钮四次,我希望看到“X1”,然后是“X1,X2”,然后是“X1,X2, X3”,然后是“X1、X2、X3、X4”。够简单吧?相反,它呈现“X1”,然后是“X1,X3”,然后是“X1,X3,X5”,然后是“X1,X3,X5,X7”。
现在我想,“嗯,也许递增 counter
的函数被调用了两次?”,所以我添加了您看到的控制台日志记录,这只会让我更加困惑。在控制台中,我看到:
Render: 0 ""
Pre-push: 0 ""
Post-push: 1 X1
Render: 1 X1
Pre-push: 1 X1
Post-push: 2 X1,X2
Render: 2 X1,X2
Pre-push: 3 X1,X3
Post-push: 4 X1,X3,X4
Render: 4 X1,X3,X4
Pre-push: 5 X1,X3,X5
Post-push: 6 X1,X3,X5,X6
Render: 6 X1,X3,X5,X6
请注意,控制台中的连接列表与 React 呈现的连接列表不匹配,没有记录 counter
如何从 2 -> 3 和 4 -> 5 跳转,并且列表的第三项神秘地改变了,尽管事实上我只追加到列表中。
值得注意的是,如果我将 ++counter
移出 setList
委托,它会按预期工作:
import React, { useState } from "react";
import "./styles.css";
let counter = 0;
export default function App() {
const [list, setList] = useState([]);
console.info("Render:", counter, list.join());
return (
<div className="App">
{list.join()}
<button
onClick={() => {
++counter;
setList((prevList) => {
console.info("Pre-push:", counter, prevList.join());
const newList = [...prevList, "X" + counter];
console.info("Post-push:", counter, newList.join());
return newList;
});
}}
>
Push
</button>
</div>
);
}
这到底是怎么回事?我怀疑这与 React 纤维和 useState
的内部实现有关,但我仍然完全不知道 counter
如何在没有控制台日志之前和之后显示证据的情况下递增这样的,除非 React 实际上正在覆盖 console
以便它可以有选择地抑制日志,这看起来很疯狂......
它似乎被调用了两次,因为它是。
当运行处于严格模式时,React intentionally invokes the following methods twice当运行处于开发模式时:
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:
- Class component constructor, render, and shouldComponentUpdate methods
- Class component static getDerivedStateFromProps method
- Function component bodies
- State updater functions (the first argument to setState)
- Functions passed to useState, useMemo, or useReducer
不确定 console.log 调用会发生什么,但我敢打赌,如果您切换到生产模式,这个问题就会消失。
老实说,除了“这里发生了什么?”之外,我正在努力想出一种表达这个问题的方法。采用以下 React 代码,旨在将增量项目添加到列表中:
import React, { useState } from "react";
import "./styles.css";
let counter = 0;
export default function App() {
const [list, setList] = useState([]);
console.info("Render:", counter, list.join());
return (
<div className="App">
{list.join()}
<button
onClick={() => {
setList((prevList) => {
console.info("Pre-push:", counter, prevList.join());
const newList = [...prevList, "X" + ++counter];
console.info("Post-push:", counter, newList.join());
return newList;
});
}}
>
Push
</button>
</div>
);
}
如果您 运行 使用 https://codesandbox.io/s/amazing-sea-6ww68?file=/src/App.js 的代码并单击“推送”按钮四次,我希望看到“X1”,然后是“X1,X2”,然后是“X1,X2, X3”,然后是“X1、X2、X3、X4”。够简单吧?相反,它呈现“X1”,然后是“X1,X3”,然后是“X1,X3,X5”,然后是“X1,X3,X5,X7”。
现在我想,“嗯,也许递增 counter
的函数被调用了两次?”,所以我添加了您看到的控制台日志记录,这只会让我更加困惑。在控制台中,我看到:
Render: 0 ""
Pre-push: 0 ""
Post-push: 1 X1
Render: 1 X1
Pre-push: 1 X1
Post-push: 2 X1,X2
Render: 2 X1,X2
Pre-push: 3 X1,X3
Post-push: 4 X1,X3,X4
Render: 4 X1,X3,X4
Pre-push: 5 X1,X3,X5
Post-push: 6 X1,X3,X5,X6
Render: 6 X1,X3,X5,X6
请注意,控制台中的连接列表与 React 呈现的连接列表不匹配,没有记录 counter
如何从 2 -> 3 和 4 -> 5 跳转,并且列表的第三项神秘地改变了,尽管事实上我只追加到列表中。
值得注意的是,如果我将 ++counter
移出 setList
委托,它会按预期工作:
import React, { useState } from "react";
import "./styles.css";
let counter = 0;
export default function App() {
const [list, setList] = useState([]);
console.info("Render:", counter, list.join());
return (
<div className="App">
{list.join()}
<button
onClick={() => {
++counter;
setList((prevList) => {
console.info("Pre-push:", counter, prevList.join());
const newList = [...prevList, "X" + counter];
console.info("Post-push:", counter, newList.join());
return newList;
});
}}
>
Push
</button>
</div>
);
}
这到底是怎么回事?我怀疑这与 React 纤维和 useState
的内部实现有关,但我仍然完全不知道 counter
如何在没有控制台日志之前和之后显示证据的情况下递增这样的,除非 React 实际上正在覆盖 console
以便它可以有选择地抑制日志,这看起来很疯狂......
它似乎被调用了两次,因为它是。
当运行处于严格模式时,React intentionally invokes the following methods twice当运行处于开发模式时:
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:
- Class component constructor, render, and shouldComponentUpdate methods
- Class component static getDerivedStateFromProps method
- Function component bodies
- State updater functions (the first argument to setState)
- Functions passed to useState, useMemo, or useReducer
不确定 console.log 调用会发生什么,但我敢打赌,如果您切换到生产模式,这个问题就会消失。