React setState详解

1.setState概念与作用

根据平时的使用来看,我们基本上会使用它来更新组件的状态state。根据官方文档的解释:

setState() 将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式.
将 setState() 视为请求而不是立即更新组件的命令。为了更好的感知性能,React 会延迟调用它,然后通过一次传递更新多个组件。React 并不会保证 state 的变更会立即生效。

按其解释,setState的作用是把组件的state更新任务排入任务队列,而不是调用就立即更新state状态,即将setState看做请求而不是立即更新组件的命令。所以在调用setState方法后就去取组件的state的值时,会取到没有更新的值。

setState() 并不总是立即更新组件。它会批量推迟更新。这使得在调用 setState() 后立即读取 this.state 成为了隐患。为了消除隐患,请使用 componentDidUpdate 或者 setState 的回调函数(setState(updater, callback)),这两种方式都可以保证在应用更新后触发。

2.使用

    1. setState(updater, [callback]),
      updater为返回stateChange对象的函数: (state, props) => stateChange
this.setState(state => ({count: state.count + 1}), () => { 
// 在状态更新且界面更新之后回调
        console.log('test3 setState callback()', this.state.count)
      })
    1. setState(stateChange, [callback])
      stateChange为对象,
      callback是可选的回调函数, 在状态更新且界面更新后才执行
this.setState({count: this.state.count + 1}), () => {
 // 在状态更新且界面更新之后回调
        console.log('test3 setState callback()', this.state.count)
      })

总结:

  • 对象方式是函数方式的简写方式
  • 如果新状态不依赖于原状态 ===> 使用对象方式
  • 如果新状态依赖于原状态 ===> 使用函数方式
  • 如果需要在setState()后获取最新的状态数据, 在第二个callback函数中读取

3.同步与异步问题

setState调用后,有些情况下就立即更新组件状态,但有时又不立即更新组件状态。这属性setState的同步和异步问题。

1.setState()更新状态是异步还是同步的

  • 同步:非react控制的异步回调函数中: 定时器回调 / 原生事件监听回调 / promise回调 /...
 /*
    定时器回调 / 原生事件监听回调 / promise回调 /...
     */
//输出顺序:...之前 count , render() count+1 , ...之后 count+1
    update2 = () => {
      setTimeout(() => {
        console.log('setTimeout setState()之前', this.state.count)
        this.setState(state => ({count: state.count + 1}))
        console.log('setTimeout setState()之后', this.state.count)
      })
    }

    update3 = () => {
      const h2 = this.refs.count
      h2.onclick = () => {
        console.log('onclick setState()之前', this.state.count)
        this.setState(state => ({count: state.count + 1}))
        console.log('onclick setState()之后', this.state.count)
      }
    }
    update4 = () => {
      Promise.resolve().then(value => {
        console.log('Promise setState()之前', this.state.count)
        this.setState(state => ({count: state.count + 1}))
        console.log('Promise setState()之后', this.state.count)
      })
    }
  • 异步:在react控制的回调函数中: 生命周期勾子 / react事件监听回调
 /*
     react事件监听回调中, setState()是异步更新状态
     */
//输出顺序:...之前 count , ...之后 count , render() count+1
    update1 = () => {
      console.log('update1 setState()之前', this.state.count)
      this.setState(state => ({count: state.count + 1}))
      console.log('update1 setState()之后', this.state.count)
    }


    /*
     react生命周期勾子中, setState()是异步更新状态
     */
    componentDidMount () {
      console.log('componentDidMount setState()之前', this.state.count)
      this.setState(state => ({count: state.count + 1}))
      console.log('componentDidMount setState()之后', this.state.count)
    }

异步的setState的多次调用问题

  • setState({}): 合并更新一次状态, 只调用一次render()更新界面 ---状态更新和界面更新都合并了
  • setState(fn): 更新多次状态, 但只调用一次render()更新界面 ---状态更新没有合并, 但界面更新合并了
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。