[英]React useState does not update value
我有点困惑为什么这个组件不能按预期工作:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1); // This effect depends on the `count` state
}, 1000);
return () => clearInterval(id);
}, []); // 🔴 Bug: `count` is not specified as a dependency
return <h1>{count}</h1>;
}
但重写如下工作:
function Counter() {
const [count, setCount] = useState(0);
let c = count;
useEffect(() => {
const id = setInterval(() => {
setCount(c++);
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
}
反应文档说:
问题是在 setInterval 回调中,count 的值没有改变,因为我们创建了一个闭包,其 count 的值设置为 0,就像效果回调运行时一样。 每秒,此回调调用
setCount(0 + 1)
,因此计数永远不会超过 1。
但解释没有意义。 那么为什么第一个代码没有正确更新计数但第二个代码呢? (也声明为let [count, setCount] = useState(0)
然后使用setCount(count++)
也可以)。
有一些提示可以帮助理解正在发生的事情。
count
是const
,所以它的范围永远不会改变。 这很令人困惑,因为它看起来在调用setCount
时发生了变化,但它从未改变,只是再次调用该组件并创建了一个新的count
变量。
当在回调中使用count
时,闭包会捕获变量并且count
保持可用,即使组件函数完成执行也是如此。 再次,它与useEffect
混淆,因为看起来回调是在每个渲染周期创建的,捕获最新的count
数值,但事实并非如此。
为清楚起见,让我们在每次创建变量时为变量添加一个后缀,看看发生了什么。
function Counter() {
const [count_0, setCount_0] = useState(0);
useEffect(
// This is defined and will be called after the component is mounted.
() => {
const id_0 = setInterval(() => {
setCount_0(count_0 + 1);
}, 1000);
return () => clearInterval(id_0);
},
[]);
return <h1>{count_0}</h1>;
}
function Counter() {
const [count_1, setCount_1] = useState(0);
useEffect(
// completely ignored by useEffect since it's a mount
// effect, not an update.
() => {
const id_0 = setInterval(() => {
// setInterval still has the old callback in
// memory, so it's like it was still using
// count_0 even though we've created new variables and callbacks.
setCount_0(count_0 + 1);
}, 1000);
return () => clearInterval(id_0);
},
[]);
return <h1>{count_0}</h1>;
}
let c
一起使用? let
可以重新分配给c
,这意味着当它被我们的useEffect
和setInterval
闭包捕获时,它仍然可以像它存在一样被使用,但它仍然是第一个定义的。
function Counter() {
const [count_0, setCount_0] = useState(0);
let c_0 = count_0;
// c_0 is captured once here
useEffect(
// Defined each render, only the first callback
// defined is kept and called once.
() => {
const id_0 = setInterval(
// Defined once, called each second.
() => setCount_0(c_0++),
1000
);
return () => clearInterval(id_0);
},
[]
);
return <h1>{count_0}</h1>;
}
function Counter() {
const [count_1, setCount_1] = useState(0);
let c_1 = count_1;
// even if c_1 was used in the new callback passed
// to useEffect, the whole callback is ignored.
useEffect(
// Defined again, but ignored completely by useEffect.
// In memory, this is the callback that useEffect has:
() => {
const id_0 = setInterval(
// In memory, c_0 is still used and reassign a new value.
() => setCount_0(c_0++),
1000
);
return () => clearInterval(id_0);
},
[]
);
return <h1>{count_1}</h1>;
}
由于很容易与所有回调和计时混淆,并且为了避免任何意外的副作用,最好使用功能更新程序状态设置器参数。
// ❌ Avoid using the captured count.
setCount(count + 1)
// ✅ Use the latest state with the updater function.
setCount(currCount => currCount + 1)
在代码中:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
// I chose a different name to make it clear that we're
// not using the `count` variable.
const id = setInterval(() => setCount(currCount => currCount + 1), 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
}
还有很多事情要做,并且需要对语言进行更多解释,以最好地准确解释它的工作原理以及它为什么会这样工作,尽管我将重点放在您的示例上以使其保持简单。
useRef
变得简单
function Counter() {
const countRef = useRef(0);
useEffect(() => {
const id = setInterval(() => {
countRef.current++;
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{countRef.current}</h1>;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.