读react官网:
状态更新可能是动态的
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
官网说这种写法是错误的
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
这种写法是正确的
我实在搞不懂为什么第一个是错误的,第二种写法是正确的,哪位大神能帮忙解释一下?在哪种需求场景下,会出现上述的情况,最好能写点代码解释下,多谢,大神们指导。
因为 this.props 和 this.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)。
setState 方法“或许”是异步的。也许你觉得,看上去更新 state 是如此轻而易举的操作,这并没有什么可异步处理的。但是要意识到,因为 state 的更新会触发 re-rendering,而 re-rendering 代价昂贵,短时间内反复进行渲染在性能上肯定是不可取的。所以,React 采用 batching 思想,它会 batches 一系列连续的 state 更新,而只触发一次 re-render。
setState的机制其实跟浏览器的dom更新类似,等到一定数量或者一定时间间隔才一起更新一次,它们都是异步更新的,所以这样做在一定几率是有问题的。
或者,直接看下面的一个小例子。
比如,最简单的一个场景是
直观上来看,当上面的 incrementMultiple 函数被调用时,组件状态的
count 值被增加了3次,每次增加1,那最后 count 被增加了3。但是,实际上的结果只给 state 增加了1。不信你自己试试~
让 setState 连续更新的几个 hack
如果想让 count 一次性加3,应该如何优雅地处理潜在的异步操作,规避上述问题呢?
以下提供几种解决方案:
方法一:常见的一种做法便是将一个回调函数传入 setState 方法中。即 setState 著名的函数式用法。这样能保证即便在更新被 batched 时,也能访问到预期的 state 或 props。(后面会解释这么做的原理)
方法二:另外一个常见的做法是需要在 setState 更新之后进行的逻辑(比如上述的连续第二次 count + 1),封装到一个函数中,并作为第二个参数传给 setState。这段函数逻辑将会在更新后由 React 代理执行。即:
方法三:把需要在 setState 更新之后进行的逻辑放在一个合适的生命周期 hook 函数中,比如 componentDidMount 或者 componentDidUpdate 也当然可以解决问题。也就是说 count 第一次 +1 之后,出发 componentDidUpdate 生命周期 hook,第二次 count +1 操作直接放在 componentDidUpdate 函数里面就好啦。
更多详细内容:从 setState promise 化的探讨 体会 React 团队设计思想