为什么 useState 屡次更新不收效?
问题
在编写 React 代码时,假如你期望屡次更新状况,可能会测验运用 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
};
经过运用函数方式的更新,咱们能够保证每次状况更新都根据前一个状况,然后完成预期的成果。