为什么 useState 多次更新不生效?

cnblogs 2024-08-18 10:41:03 阅读 99

问题

在编写 React 代码时,如果你希望多次更新状态,可能会尝试使用 <code>handleClickWrong 中的方式。然而,你会发现实际效果并不如预期:count 只增加了 1,而不是 3。

const root = document.getElementById('root');

const App = () => {

return (

<div>

<h1>Hello World</h1>

<Counter />

</div>

);

};

const Counter = () => {

const [count, setCount] = React.useState(0);

const handleClickWrong = () => {

setCount(count + 1);

setCount(count + 1);

setCount(count + 1);

};

return (

<div>

<h1>{count}</h1>

<button onClick={handleClickWrong}>Increment</button>

</div>

);

};

const rootElement = ReactDOM.createRoot(root);

rootElement.render(<App />);

为什么会出现这种情况呢?要弄清楚这个问题,我们需要了解 React 如何处理状态更新的细节。

React 内部状态更新机制

React 使用了一个内部机制来处理状态更新。我们可以通过以下源码片段了解这背后的实现:

// React 源码中的基本状态处理器

function basicStateReducer(state, action) {

// $FlowFixMe: Flow doesn't like mixed types

return typeof action === 'function' ? action(state) : action;

}

在我们使用 handleClickWrong 的情况下,React 会按照以下方式处理状态更新:

// 假设初始状态为 0,我们传递的值是 1

newState = basicStateReducer(0, 1);

由于 basicStateReducer 接收到的 action 不是函数,它会直接返回这个值作为最新状态。因此,newState 会被更新为 1。

回到 handleClickWrong 的例子:

const handleClickWrong = () => {

setCount(count + 1); // = basicStateReducer(0, 1) => 1

setCount(count + 1); // = basicStateReducer(0, 1) => 1

setCount(count + 1); // = basicStateReducer(0, 1) => 1

};

由于 count 在当前 Counter 函数的作用域内,三次 setCount 调用的值都是 count + 1。因此,即使调用了多次 setCount,由于 React 只处理了最新的 count 值,结果每次都将状态更新为 1。最终渲染时,count 依然只有 1。

解决方案

了解了原因后,我们可以改用函数形式的更新来解决这个问题。注意到 basicStateReducer 会检查传入的 action,如果是函数,React 会用当前内部存储的状态作为参数传给这个函数。这样,我们可以基于最新的状态进行更新。

const handleClick = () => {

setCount((prevCount) => prevCount + 1); // = basicStateReducer(0, (prevCount) => prevCount + 1) => 0 + 1 => 1

setCount((prevCount) => prevCount + 1); // = basicStateReducer(1, (prevCount) => prevCount + 1) => 1 + 1 => 2

setCount((prevCount) => prevCount + 1); // = basicStateReducer(2, (prevCount) => prevCount + 1) => 2 + 1 => 3

};

通过使用函数形式的更新,我们可以确保每次状态更新都基于前一个状态,从而实现预期的结果。


上一篇: 突破编程_前端_SVG(circle 圆形)

下一篇: 02-QWebEngineView的使用

本文标签

React   


声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。