Skip to content
On this page

setState

正确使用 setState

setState(partialState,callback)

  • partialState: object | function

用于生产当前 state 合并的子集

  • callback:function

state 更新之后被调用。

修改 state

在 react 中,我们不能直接通过修改 state 的值来让界面发生更新:

  • 在 React 根据最新的 State 来重新渲染界面,但是这种方式的修改 React 并不知道数据发生了变化;
  • React 并没有实现类似于 Vue2 中的 Object.defineProperty 或者 Vue3 中的 Proxy 的方式来监听数据的变化;
  • 我们必须通过 setState 来告知 React 数据已经发生了变化;

不要直接修改 State

jsx
// 错误
this.state.comment = 'Hello';

// 正确
this.setSate({comment:'Hello'});
1
2
3
4
5

setState 源码

setState 方法是从 Component 中继承过来的。

javascript
Component.prototype.setstate= function(partialstate,callback)
invariant(
    typeof partialState === 'object' ||
    typeof partialState === ' function' ||
    partialState == null,
'setstate(...):takes an object of state variables to update or a !  +
'function which returns an object of state variables.!',
);
this.updater.enqueueSetState(this, partialstate,callback,'setState'):
1
2
3
4
5
6
7
8
9

Setstate(…): 接受一个状态变量对象来更新或一个!+ 函数返回一个状态变量对象。

State 的更新可能是异步的

处于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。 观察以下例子中 log 的值和 button 显示的 counter

javascript
import React, { Component } from "react";
export class SetStatePage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 0,
    };
  }
  changeValue = (v) => {
    this.setDate({
      counter: this.state.counter + v
    })
  };
  setCounter = () => {
    this.changeValue(1);
  };
  render() {
    return (
      <div>
        <h3>SetStatePage</h3>
        <button onClick={this.setCounter}>{this.state.counter}</button>
      </div>
    );
  }
}

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

为什么 setState 设计为异步呢?

setState 设计为异步,可以显著的提升性能;

  • 如果每次调用 setState 都进行一次更新,那么意味着 render 函数会被频繁调用,界面重新渲染,这样效率是很低的;
  • 最好的办法应该是获取到多个更新,之后进行批量更新;

如何获取异步的结果

在回调中获取状态值

jsx
changeValue = (v) => {
  // setState在合成事件中是异步的,这里异步其实就是批量的更新,达到优化的目的
  this.setState(
    {
      counter: this.state.counter + v,
    },
    () => {
      console.log("counter", this.state.counter);
    }
  );
};
1
2
3
4
5
6
7
8
9
10
11

生命周期

jsx
componentDidupdate (prevProps, provState, snapshot){
    console.log(this.state.message)
}
1
2
3

使用定时器

在 setTimeout 或者原生 dom 事件中,setState 是同步

javascript
setTimeout(()=>{
	this.setCounter();
},0)
1
2
3

原生事件中修改状态

在组件生命周期或 React 合成事件中,setState 是异步

javascript
componetDidMount(){
	document.body.addEventListener('click',this.changeValue, false)
}
1
2
3

总结: setState 只有在合成事件和⽣生命周期函数中是异步的,在原⽣生事件和 setTimeout 中都是同步的,这⾥里里的异步其实是批量量更更新。

多个 state 的合并

jsx
// 如果有多个 state
function incrementMultiple() {
  this.setState({count: this.state.count + 1});
  this.setState({count: this.state.count + 1});
  this.setState({count: this.state.count + 1});
}
1
2
3
4
5
6

this.state.count 的值为多少呢?答案为 1

那哪些情况是不会合并呢?

jsx
this.setState((prevState, props) => {
    return {
        count: prevState.count + 1
    }
})
this.setState((prevState, props) => {
    return {
        count: prevState.count + 1
    }
})
this.setState((prevState, props) => {
    return {
        count: prevState.count + 1
    }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

传入函数,不会被合并。执行结果是 +3

沪ICP备20006251号-1