我在这篇官方文章中读过这些文字:
this.props
和this.state
可能会异步更新,您不应该依赖它们的值来计算下一个状态。
任何人都可以通过举例说明以下代码试图实现的目的。
this.setState((prevState, props) => ({
couter: prevState.counter + props.increment
}));
答案 0 :(得分:16)
他们说你应该这样做,而不是下面的例子。
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
如果您这样访问,他们无法确保状态具有正确的值,因为 setState()将异步发生,可能会发生其他更新并更改该值。如果你要根据以前的状态计算状态,你必须确保你有最后一个和最新的值,所以他们让 setState()接受一个被调用的函数 prevState 和道具,因此您可以使用正确的值来更新您的州,例如下面的示例。
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
答案 1 :(得分:4)
要添加Bruno的答案,上面的正确函数称为纯函数。 React在名为immutability的东西上很重要,这意味着如果可能的话,每个声明的值都不应该从其原始声明中更改。在传递它们之前,该函数中的变量不是您的实际道具和状态,这意味着在javascript函数堆栈(排队同步和异步调用的线程)上,属性的值和引用将以不同方式存储,从而产生不确定性在“错误”的案件中价值是多少。
答案 2 :(得分:1)
React可以将多个setState()调用批处理为一个更新,以提高性能。
由于
this.props
和this.state
可能是异步更新的,因此不应依赖于它们的值来计算下一个状态。
并且来自https://reactjs.org/docs/react-component.html#setstate:
setState()
使更改进入组件状态并告知React 该组件及其子组件需要使用 更新状态。将
setState()
视为请求,而不是立即命令 更新组件。为了获得更好的感知性能,React可能会 延迟它,然后一次通过更新几个组件。反应 不保证状态更改会立即应用。
了解示例
这个概念可能很难理解,特别是为什么它会引起问题,所以我写了一个例子来说明发生的错误:
/* Imagine props and states is the same as this.props and this.state */
var state = { counter: 0 } ; var props = { } ;
/* Our fake implementation of react setState */
var setStatesToRun = []
function setState(myFunction) { setStatesToRun.push(myFunction); }
/* Our fake implementation of react batch update */
function batchRunStateUpdates() {
propsLocal = props
stateLocal = state
f1 = setStatesToRun.pop()
newState = f1(stateLocal, propsLocal) // Will run increment by 3
console.log(newState) // newState: { counter: 3 }
console.log(state) // state: { counter: 0 }
f2 = setStatesToRun.pop()
newState = f2(newState, propsLocal) // Will run increment by 2
console.log(newState) // newState: { counter: 2 }
console.log(state) // state: { counter: 0 }
// ... get the next setState function loop
console.log("Will update global state")
state = newState
console.log(state) // state: { counter: 2 } // WRONG!
}
console.log(setStatesToRun) // []
// Right
setState((prevState, props) => { counter: prevState.counter + 3 });
// WRONG, using state (this.state)
setState((prevState, props) => { counter: state.counter + 2 });
console.log(setStatesToRun) // [func, func]
batchRunStateUpdates();
在顶部,我们有一些React方法的伪造版本。这些过分简化,但有助于解释会发生什么。
然后,我们像在React中一样使用setState。一种用法是正确的,另一种是错误的。
请注意,最终的全局状态应为state: { counter: 5 }
,但是由于我们不尊重React的建议,我们得到了state: { counter: 2 }