# setState 详解

# 异步更新

setState用来更新 state 进而触发 re-render 更新视图.可是它是异步更新, 意思是我调用setState后, this.state并没有随即发生变化:

0

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0
    };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState({ n: this.state.n + 1 });
    this.setState({ n: this.state.n + 1 });
    this.setState({ n: this.state.n + 1 });
    console.log("handleClick: ", this.state.n);
  }
  render() {
    console.log("render函数: ", this.state.n);
    return (
      <>
        <p>{this.state.n}</p>
        <button onClick={this.handleClick}>+1</button>
      </>
    );
  }
}
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

我在handleClick里连续三次使用了setState结果它只更新了一次.要理解这种现象, 要先改变对setState的理解, 要把它看作 请求(request) 而非立即更新组件的命令. 当调用setState后会发起更新请求, React 对请求进行批处理(batch), 换句话说, 更新请求会先被搁置, 也许过一会儿又有几个更新请求过来.就像下面的小朋友们一样排队等候.然后 React 再一起处理.

wash

对于传入对象参数的调用方式, React 会将后面的对象会覆盖之前的对象, 这等价于:

var newObj = Object.assign(
  {},
  { name: "jobs", age: 12 },
  { name: "jobs", age: 12 }
);

console.log(newObj); // {name: 'jobs', age: 12}
Copied!
1
2
3
4
5
6
7

因此, 连续三次调用this.setState({ n: this.state.n + 1 });只会被合并成一次this.setState({ n: this.state.n + 1 });, 所以在控制台里看到更新的n1.

# 什么时候更新 state

既然不能立即更新state,那么什么时候会呢? 我通过打 log 的方法在各个生命周期里发现staterender 函数里更新了. 但是当shouldComponentUpdate返回的是false时,state仍然会更新.

# 为何要异步更新

有以下两点原因:

  1. 目前 React 会将 setState 的效果放在队列中, 积攒着一次引发更新过程, 为的就是把 Virtual DOM 和 DOM 树操作降到最小, 用于提高性能.

  2. shouldComponentUpdate需要用this.state来保留更新前的状态.一旦setState改为同步更新, shouldComponentUpdate也就废掉了.

# 传入函数的setState

不多逼逼, 先上代码:

0

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0
    };
    this.handleClick = this.handleClick.bind(this);
    this.add = this.add.bind(this);
  }
  add(state) {
    return { n: state.n + 1 };
  }
  handleClick() {
    this.setState(this.add);
    this.setState(this.add);
    this.setState(this.add);
    console.log("handleClick: ", this.state.n);
  }
  render() {
    console.log("render函数: ", this.state.n);
    return (
      <>
        <p>{this.state.n}</p>
        <button onClick={this.handleClick}>+1</button>
      </>
    );
  }
}
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

这下, state就一下子累加到3了, React 通过函数的方式, 用参数state进行累加, 保证了下次调用setState时的参数state已经更新(注意: this.state并没有更新), 最后将参数state更新到this.state上. 这太牛逼了.