guoshuai93 / blog

19 stars 2 forks source link

React setState 是同步还是异步方法?setState 传递对象和函数区别? #43

Open guoshuai93 opened 3 years ago

guoshuai93 commented 3 years ago

React setState 是同步还是异步方法?setState 传递对象和函数区别?

先看一个场景:

state = {
  count: 0
}

componentDidMount () {
  this.setState({count: 1})
  console.log(this.state.count) // 0
}

这里输出的怎么还是 0,我们不是已经更新了吗?虽然这里比较像异步的形式,其实这是由 React 框架本身的性能优化机制所导致的:将多个状态合并一起更新,而不是每次 setState 就更新一次,减少 re-render。当然你也可以理解为更新 state 是一个异步的操作。

setState 方法传递参数为对象或者函数

Counter Class 版

看下面这个计数器:inc 方法每次更新只会加上 1,incMulti 方法每次更新则符合预期地加上 2,为什么呢?

class Counter extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      count: 0
    }
  }

  inc () {
    this.setState({count: this.state.count+1})
    this.setState({count: this.state.count+1})
  }

  incMulti () {
    this.setState((state, props) => ({count: state.count+1}))
    this.setState((state, props) => ({count: state.count+1}))
  }

  render () {
    return (<div>
      Class Demo: <p>{this.state.count}</p>
      <button onClick={() => this.inc()}>+</button>
      <button onClick={() => this.incMulti()}>+</button>
    </div>)
  }
}

Counter Hooks 版

参照下面以 Hooks 实现的例子,和 Class 写法的组件实现的效果是一样的,只是 setCountsetState 的参数不一样而已。

function CounterFn() {
  const [count, setCount] = useState(0)

  function inc() {
    setCount(count+1)
    setCount(count+1)
  }

  function incMulti() {
    setCount(count => count+1)
    setCount(count => count+1)
  }

  return <div>
    Hooks Demo: <p>{count}</p>
    <button onClick={inc}>+</button>
    <button onClick={incMulti}>+</button>
  </div>
}

如何能同步读取到最新状态

  1. setState / useState 函数式更新状态:如 this.setState((state, props) => ({count: state.count+1}))
  2. setState 支持第二个参数为完成回调函数,可以读取到最新的 state
  3. useRef Hook:把状态保存到 ref 实例上的 current 属性上(实时改变,不触发 re-render)