我们先来看一个问题
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
num: 0,
};
}
componentWillMount() {
this.setState({
num: this.state.num + 1,
});
console.log(this.state.num);
this.setState({
num: this.state.num + 1,
});
console.log(this.state.num);
}
render() {
console.log(this.state.num);
return (<div />);
}
}
export default Test;
在上面这段代码中3个console.log()分别打印出来的num是多少呢?
很多人会认为是:1 2 2,但是打印出来的是0 0 1。
所以我们可以很确定认为setState这个在React中用来更新state的方法是一个异步的方法。
setState异步更新
setState方法通过一个队列机制实现state更新,当执行setState的时候,会将需要更新的state合并之后放入状态队列,而不会立即更新this.state(可以和浏览器的事件队列类比)。
setState简化调用栈:
那么我们要如何解决上面的问题呢?
1、利用setTimeout
componentWillMount() {
setTimeout(() => {
this.setState({
num: this.state.num + 1,
});
console.log(this.state.num); // 1
this.setState({
num: this.state.num + 1,
});
console.log(this.state.num); // 2
}, 0);
}
2、利用setState的回调函数
在React的官方文档中:
setState(updater[, callback])
updater的函数如下
(prevState, props) => stateChange
所以我们可以通过回调去实时获取它更新的值:
componentWillMount() {
this.setState(((num) => {
num.num++;
}), () => {
console.log(this.state.num); // 2
});
this.setState(((num) => {
num.num++;
}), () => {
console.log(this.state.num); // 2
});
}
3、利用Promise,进行封装
setStatePromise(updator) {
return new Promise(((resolve, reject) => {
this.setState(updator, resolve);
}));
}
componentWillMount() {
this.setStatePromise(({ num }) => ({
num: num + 1,
})).then(() => {
console.log(this.state.num);
});
}
注意事项:
1、在State更新时,如果更新的值涉及到了state和props,我需要注意在调用setState时不要直接使用this.props和this.state,在React文档中这样说到:
React may batch multiple setState() calls into a single update for performance.
// React可能会将多个setState()调用批量处理为单个更新以提高性能。
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
// 因为this.props和this.state可能会异步更新,所以不应该依赖它们的值来计算下一个状态。
那么我们怎样去解决这个问题呢?
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
2、如果在shouldComponentUpdate或者componentWillUpdate方法中调用setState,此时this._pending-StateQueue != null,就会造成循环调用,使得浏览器内存占满后崩溃。(亲测有效)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。