React中的setState:异步、同步、宏任务与微任务

381 阅读3分钟

在React中,setState的行为可能会让人感到有些迷惑,因为它在不同情况下表现不同。通常,setState在React的生命周期方法和事件处理函数中表现异步,而在其他情况下(如setTimeoutsetInterval、原生事件处理函数或Promise中)则表现同步。

不敢说大话,对于React setState, 掌握以下5点你就够了!!!

1. setState在React生命周期和事件处理中的行为

当你在React组件的构造函数、生命周期方法或事件处理函数中调用setState时,React会将新的状态更新放入一个队列中,并稍后批量处理这些更新。这样做是为了提高性能,避免在短时间内多次渲染组件。因此,setState不会立即更新组件的状态,而是异步执行的。

handleClick = () => {
  console.log('1');
  this.setState({
    count: this.state.count + 1
  }, () => {
    console.log('2');
  });
  console.log('3');
};
// 输出顺序: 1, 3, 2

在这个例子中,setState被调用后,状态更新会被放入队列中,但不会立即执行。事件处理函数继续执行,直到所有同步代码执行完毕后,React才会处理状态更新队列。这就是为什么console.log('2')会在console.log('3')之后执行。

2. setState在宏任务和微任务中的行为

当你在宏任务(如setTimeout)或微任务(如Promisethenasync/await)中调用setState时,它表现得像同步代码一样,因为这时React不在控制调用栈。

handleClick = () => {
  setTimeout(() => {
    console.log('1');
    this.setState({
      count: this.state.count + 1
    }, () => {
      console.log('2');
    });
    console.log('3');
  });
};
// 输出顺序: 1, 2, 3

在这个例子中,setTimeout将代码推迟到当前事件循环的末尾,此时React已经处理了所有状态更新。因此,setState会立即更新状态,并立即执行回调函数。

3. setState的使用方法

setState可以接受一个对象或一个函数作为第一个参数。如果传递一个函数,这个函数将接受先前的状态作为第一个参数,并返回一个更新后的状态对象。

// 使用对象
this.setState({ count: this.state.count + 1 });
// 使用函数
this.setState(prevState => ({ count: prevState.count + 1 }));

4. setState的回调函数

setState还接受一个可选的回调函数作为第二个参数,这个回调函数将在状态更新且组件重新渲染后执行。

this.setState({ count: this.state.count + 1 }, () => {
  console.log('状态已更新:', this.state.count);
});

5. 批量更新优化

React通过批量更新优化性能。当你连续多次调用setState时,React会将这些状态更新合并为一个更新,只调用一次组件的render方法。

handleClick() {
  this.setState((prevState) => ({ count: prevState.count + 1 }));
  this.setState((prevState) => ({ count: prevState.count + 1 }));
  this.setState((prevState) => ({ count: prevState.count + 1 }));
}

在这个例子中,尽管我们连续调用了三次setState,但最终count只会增加1,因为React将这三次更新合并为了一个。

总结来说,理解setState的行为对于高效使用React至关重要。它通常表现为异步操作,以提高性能,但在某些情况下也可以表现得像同步操作。正确使用setState和它的回调函数可以帮助我们更有效地管理组件的状态。