par*_*aks 3 javascript reactjs react-hooks use-effect use-state
我有点困惑为什么这个组件不能按预期工作:
\nfunction Counter() {\n const [count, setCount] = useState(0);\n\n useEffect(() => {\n const id = setInterval(() => {\n setCount(count + 1); // This effect depends on the `count` state\n }, 1000);\n return () => clearInterval(id);\n }, []); // Bug: `count` is not specified as a dependency\n\n return <h1>{count}</h1>;\n}\nRun Code Online (Sandbox Code Playgroud)\n但重写如下:
\nfunction Counter() {\n const [count, setCount] = useState(0);\n let c = count;\n useEffect(() => {\n const id = setInterval(() => {\n setCount(c++);\n }, 1000);\n return () => clearInterval(id);\n }, []);\n\n return <h1>{count}</h1>;\n}\nRun Code Online (Sandbox Code Playgroud)\nReact 文档说:
\n\n\n问题是在 setInterval 回调内部,count 的值没有改变,因为我们创建了一个闭包,其中 count 的值设置为 0,就像效果回调运行时一样。每一秒,这个回调都会调用
\nsetCount(0 + 1),因此计数永远不会超过 1。
但这个解释没有意义。那么为什么第一个代码没有正确更新计数,而第二个代码却可以呢?\n(也声明为let [count, setCount] = useState(0)then usingsetCount(count++)也可以正常工作)。
Emi*_*ron 10
有一些提示可以帮助您了解正在发生的情况。
\ncount是const,所以它的范围永远不会改变。这很令人困惑,因为它看起来在调用时正在改变setCount,但它永远不会改变,只是再次调用该组件并count创建一个新变量。
当count在回调中使用时,闭包会捕获变量并count保持可用,即使组件函数已完成执行也是如此。同样,这很令人困惑useEffect,因为看起来回调是在每个渲染周期创建的,捕获最新的count值,但事实并非如此。
为了清楚起见,让我们在每次创建变量时为变量添加一个后缀,看看会发生什么。
\nfunction Counter() {\n const [count_0, setCount_0] = useState(0);\n\n useEffect(\n // This is defined and will be called after the component is mounted.\n () => {\n const id_0 = setInterval(() => {\n setCount_0(count_0 + 1);\n }, 1000);\n return () => clearInterval(id_0);\n }, \n []);\n\n return <h1>{count_0}</h1>;\n}\nRun Code Online (Sandbox Code Playgroud)\nfunction Counter() {\n const [count_1, setCount_1] = useState(0);\n\n useEffect(\n // completely ignored by useEffect since it\'s a mount \n // effect, not an update.\n () => {\n const id_0 = setInterval(() => {\n // setInterval still has the old callback in \n // memory, so it\'s like it was still using\n // count_0 even though we\'ve created new variables and callbacks.\n setCount_0(count_0 + 1);\n }, 1000);\n return () => clearInterval(id_0);\n }, \n []);\n\n return <h1>{count_1}</h1>;\n}\nRun Code Online (Sandbox Code Playgroud)\nlet c?let使得重新分配给 成为可能c,这意味着当它被 ouruseEffect和setInterval闭包捕获时,它仍然可以像它存在一样使用,但它仍然是第一个定义的。
function Counter() {\n const [count_0, setCount_0] = useState(0);\n\n let c_0 = count_0;\n\n // c_0 is captured once here\n useEffect(\n // Defined each render, only the first callback \n // defined is kept and called once.\n () => {\n const id_0 = setInterval(\n // Defined once, called each second.\n () => setCount_0(c_0++), \n 1000\n );\n return () => clearInterval(id_0);\n }, \n []\n );\n\n return <h1>{count_0}</h1>;\n}\nRun Code Online (Sandbox Code Playgroud)\nfunction Counter() {\n const [count_1, setCount_1] = useState(0);\n\n let c_1 = count_1;\n // even if c_1 was used in the new callback passed \n // to useEffect, the whole callback is ignored.\n useEffect(\n // Defined again, but ignored completely by useEffect.\n // In memory, this is the callback that useEffect has:\n () => {\n const id_0 = setInterval(\n // In memory, c_0 is still used and reassign a new value.\n () => setCount_0(c_0++),\n 1000\n );\n return () => clearInterval(id_0);\n }, \n []\n );\n\n return <h1>{count_1}</h1>;\n}\nRun Code Online (Sandbox Code Playgroud)\n由于很容易与所有回调和计时混淆,并且为了避免任何意外的副作用,最好使用功能更新器状态设置器参数。
\n// \xe2\x9d\x8c Avoid using the captured count.\nsetCount(count + 1)\n\n// \xe2\x9c\x85 Use the latest state with the updater function.\nsetCount(currCount => currCount + 1)\nRun Code Online (Sandbox Code Playgroud)\n在代码中:
\nfunction Counter() {\n const [count, setCount] = useState(0);\n\n useEffect(() => {\n // I chose a different name to make it clear that we\'re \n // not using the `count` variable.\n const id = setInterval(() => setCount(currCount => currCount + 1), 1000);\n return () => clearInterval(id);\n }, []);\n\n return <h1>{count}</h1>;\n}\nRun Code Online (Sandbox Code Playgroud)\n还有更多的事情发生,并且需要更多的语言解释来最好地解释它是如何工作的以及为什么它这样工作,尽管我将其重点放在您的示例上以保持简单。
\n| 归档时间: |
|
| 查看次数: |
7153 次 |
| 最近记录: |