Skip to main content

The setState in React

· 2 min read
Lex
Front End Engineer @ Baoxiaohe

What#

在使用 React 时,并不能通过直接修改state里的数据使页面发生更新。React 没有像 Vue 一样使用Object.defineProperty或者 Proxy 的方式来监听数据变化。所以必须通过 setState方法告知 React 数据已经发生了更改。

该方法是从 Component 继承来的,源码中有具体实现。

异步更新#

异步更新很好理解,

this.state = {  message: "Hello World"}// ...this.setState({  message: "Hello React"})console.log(this.state.message) // Hello World

虽然已经告知 React 数据更新了,log 结果仍然是更新前的 message。这就是因为setState 是非同步的。

Why is setState asynchronous#

  • 显著提高性能
  • 使props和state保持同步
每次调用 setState 方法同步调用 render 函数重新渲染页面效率是很低的,最好是在获取多次 更新后批量 `render`。

U can know more in here

How get synchronized data#

  • 1、get it in callback function
this.setState({  message: "Hello React"},() => {  console.log(this.state.message) // Hello React})
  • 2、use lifecycle function 当调用render function 执行完后会回调 componentDidUpdate这个生命周期函数。👇
componentDidUpdate() {  console.log(this.state.message) // Hello React}

了解更多lifecycle function

Must setState be asynchronous#

  • 在组件生命周期或 React 合成事件(onClick)中,setState是异步;
  • setTimeout或原生 DOM 事件中,setState是同步🕵️‍♂️
changeText() {  setTimeout(() => {    this.setState({      message: "你好呀,李银河"    })    console.log(this.state.message) // 你好呀,李银河  }, 0)}
componentDidMount() {  const btnEl = document.getElementById("btn")  btnEl.addEventListener('click',() => {    this.setState({      message: "你好呀,李银河"    })    console.log(this.state.message) // 你好呀,李银河  })}

React 源码中对这两种情况进行了判断,从而导致不同的处理方式。

数据合并#

setState函数传入对象作为参数,为什么新的对象不对旧对象完全覆盖,而只是修改了同名属性。 其实是因为源码中使用了Object.assign() 函数。

// Object.assign({},prevState,partialState)Object.assign({},this.state,{message: "你好呀,李银河"})

Why should not change state#

React 明确说明不推荐在 setState 中对 state 原数据(通常是引用类型)进行修改。 原因在于性能优化(或者 extends PureComponent)时会有问题🕵️‍♂️

shouldComponentUpdate(newProps, newState) {  // 修改相同引用类型(Array)数据并不会走下行逻辑  if(newState.books !== this.state.books){    return true   }  return false}

That's all