Sea*_*ean 1 javascript reactjs react-hooks
我知道 React 组件内的嵌套函数或回调(例如 setTimeout)存在陈旧状态问题。例如,在下面的代码(取自 React 文档)中,我们看到了陈旧状态的问题:
\n\n\n如果您首先单击 \xe2\x80\x9cShow Alert\xe2\x80\x9d,然后递增计数器,\nalert 将显示您单击 \xe2\x80\x9cShow\nalert\xe2\x80 时的计数变量\x9d 按钮。”
\n
function Example() {\n const [count, setCount] = useState(0);\n\n function handleAlertClick() {\n setTimeout(() => {\n alert('You clicked on: ' + count);\n }, 3000);\n }\n\n return (\n <div>\n <p>You clicked {count} times</p>\n <button onClick={() => setCount(count + 1)}>\n Click me\n </button>\n <button onClick={handleAlertClick}>\n Show alert\n </button>\n </div>\n );\n}\nRun Code Online (Sandbox Code Playgroud)\n我知道我们可以使用以下方法创建对最新/当前状态值的可变引用useRef:
function Example() {\n const [count, setCount] = useState(0);\n const stateRef = useRef()\n stateRef.current = count\n function handleAlertClick() {\n setTimeout(() => {\n alert('You clicked on: ' + stateRef.current);\n }, 3000);\n }\n\n return (\n <div>\n <p>You clicked {count} times</p>\n <button onClick={() => setCount(count + 1)}>\n Click me\n </button>\n <button onClick={handleAlertClick}>\n Show alert\n </button>\n </div>\n );\n}\nRun Code Online (Sandbox Code Playgroud)\nuseRef我的问题是,为什么创建对变量 ( )的引用stateRef可以解决陈旧状态的问题,以及为什么这可以解决闭包问题?count当然,通过直接在内部引用setTimeout,一旦组件重新渲染,count值就会更新,因此我们对它的引用将返回最新值。
我很难理解为什么引用stateRef而不是count绕过陈旧状态的问题,因为从setTimeouts 的角度来看它们都是在相同的词法范围中声明的。
尝试想象一个时间轴,组件的每次重新渲染都会创建一个“快照”并在此时间轴上留下标记。
所谓的“陈旧状态”更常被称为“陈旧闭包”,它更准确地描述了真正的问题。
关闭究竟如何造成麻烦?
每次组件重新渲染时,组件函数本身都会从头到尾重新执行,在执行过程中:
如果您调用setTimeout并向其发送回调函数,并且该函数引用局部变量,则它实际上存储在渲染时间轴上的这些快照之一内。
当组件重新渲染时,新的快照会添加到时间线中,并且只有最新的快照是最新的,而回调函数仍然引用过时的快照/闭包。
当setTimeout决定执行回调时,回调会查看自己的快照/闭包版本,并说“好吧,'count'变量为 0”,但不知道它已经过时了。
useRef问题如何解决?
Cusconst ref = useRef()在重新渲染时始终返回相同的对象引用。因此,即使回调查看过时的快照/闭包,它仍然会看到ref与最新快照中相同的对象。并且由于组件函数的每次重新执行总是将属性设置ref.value = someValue为最新值,因此回调有一种访问最新值的方法。