【发布时间】:2021-06-01 12:24:29
【问题描述】:
由于 React setState 和经过大量研究后新引入的 react concurrent mode 的异步特性,我不知道如何在以下场景或任何其他场景中访问保证的最新状态。
非常感谢 React 专家的回答,尤其是来自 React 团队的回答。
请记住,这是一个非常简单的示例,真正的问题可能发生在一个充满状态更新、事件处理程序、改变状态的异步代码的复杂项目中......
import { useCallback, useState } from "react";
const Example = ({ onIncrement }) => {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
onIncrement(count, count + 1); // Is count guaranteed to be the latest state here due to including count in the useCallback dependency array?
setCount((count) => count + 1);
}, [count, onIncrement]);
return (
<>
<span>{count}</span>
<button onClick={increment}>increment</button>
</>
);
};
const Parent = () => (
<Example
onIncrement={(currentCount, incrementedCount) =>
console.log(
`count before incrementing: ${currentCount}, after increment: ${incrementedCount}`
)
}
/>
);
export default Parent;
你可能会说我可以在setCount 回调中调用onIncrement,但是由于新的react 并发模式,你可以看到将来react 更新onIncrement 可能会被调用两次并输出两次结果,即不是想要的结果。
提交阶段通常非常快,但渲染可能很慢。出于这个原因,即将到来的并发模式(默认情况下尚未启用)将渲染工作分成几部分,暂停和恢复工作以避免阻塞浏览器。这意味着 React 可以在提交之前多次调用渲染阶段生命周期,或者它可以在根本不提交的情况下调用它们(因为错误或更高优先级的中断)。
你已经可以(React 17 strict mode)看到onIncrement在开发模式和React并发模式成为默认后的特性中会被调用两次,所以在生产中可能会以这种方式调用两次。
在 useCallback 依赖数组中包含 count 是否保证 count 是最新的状态值?
您可以建议在useEffect 挂钩中调用onIncrement,但这样我将无法访问先前的状态,与此示例不同,它可能无法重新计算。 (使用 ref 存储之前的状态不是解决方案)
使用useEffect 的另一个问题是我不确定这个事件处理程序(onIncrement) 是导致该效果的原因还是另一个处理程序中的状态更改或useEffect 回调导致了该效果。 (存储额外的状态或参考来检测原因是多余的)
谢谢!
【问题讨论】:
-
为什么不能在 setCount 回调中调用 onIncrement?
-
由于答案中解释的新并发模式,它可能在生产中被调用两次,并且它将在严格模式下被调用两次以避免将来出现错误。
-
啊抱歉,看错了那部分,实际上假设情况并非如此。似乎 React 再次完全进化了……你说“使用 ref 存储先前的状态不是解决方案”,但实际上这听起来是个好主意?你不想要裁判的理由是什么?
-
抱歉回复晚了。问题是,如果我选择将先前的状态存储在 ref 中,那么我将不得不在
useEffect回调中调用onIncrement以访问最新状态,并且我将不得不使用另一个状态或 ref 来检测它点击事件是效果的原因,而不是改变计数状态的另一个事件或代码。当你有这么多这样的代码时,你的代码会被额外的 useState、useRef 和 useEffect 钩子污染。 -
由于
increment函数是由用户事件触发的,我想说count是用户在单击按钮时看到的最新值。但我看到有些情况下需要“最新值”而不是“最新渲染值”,在这种情况下,“已知钩子”的所有组合都不合适。也许这值得向 React 团队提出一个问题,因为我认为这只能通过新的“引用状态”来解决
标签: javascript reactjs state setstate usecallback