关于javascript:React系列九深入理解setState

34次阅读

共计 5435 个字符,预计需要花费 14 分钟才能阅读完成。

快来退出咱们吧!

“ 小和山的菜鸟们 ”,为前端开发者提供技术相干资讯以及系列根底文章。为更好的用户体验,请您移至咱们官网小和山的菜鸟们 (https://xhs-rookies.com/) 进行学习,及时获取最新文章。

“Code tailor”,如果您对咱们文章感兴趣、或是想提一些倡议,微信关注 “小和山的菜鸟们” 公众号,与咱们取的分割,您也能够在微信上观看咱们的文章。每一个倡议或是同意都是对咱们极大的激励!

前言

这节咱们将介绍 ReactsetState,心愿能够帮忙大家真正了解 setState

本文会向你介绍以下内容:

  • 如何应用 setState
  • 不能间接批改 State
  • setState()
  • setState 可能是异步更新
  • setState 的合并

如何应用 setState

在介绍 setState 之前,咱们先来看一个 setState 的案例,理解一下是如何应用的。

咱们来展现一个应用案例,当点击一个 扭转文本 的按钮时,批改界背后显示的内容:

案例代码

import React, {Component} from 'react'

export default class App extends Component {constructor(props) {super(props)

    this.state = {message: 'Hello React',}
  }

  render() {
    return (
      <div>
        <h2>{this.state.message}</h2>
        <button onClick={(e) => this.changeMessage()}>ChangeMessage</button>
      </div>
    )
  }

  changeMessage() {
    this.setState({message: 'Hello xhs,your message is changed.',})
  }
}

state 的初始化是在类结构器中去设置的,而后如果想要更改 state,那就是通过 setState 函数,该函数最次要的就是传入一个对象作为参数,而该对象就是你想要批改的值。上述代码中,当你点击 ChangeMessage 时,就会调用 setState 函数,而 setState 会调用 render 函数,页面就会被从新渲染。

点击按钮之后,从新渲染的成果:

不能间接批改 State

将下面的 changeMessage 办法,改成上面的样子。

changeMessage() {this.state.message = "Hello xhs,your message is changed.";}

点击 ChangeMessage 之后,页面并没有扭转,然而打印 state 你会发现,state 中的 message 变了,然而页面不会被从新渲染。

构造函数,是咱们惟一能够给 this.state 赋值的中央,而为了能够从新渲染页面,那就只能通过 setState 函数来批改。

所以,咱们通过调用 setState 来批改数据:

  • 当咱们调用 setState 时,会从新执行 render 函数,依据最新的 State 来创立 ReactElement 对象
  • 再依据最新的 ReactElement 对象,对 DOM 进行批改;
changeMessage() {
  this.setState({message: "Hello xhs,your message is changed."})
}

setState()

setState(updater, [callback])

setState() 将对组件 state 的更改排入队列,并告诉 React 须要应用更新后的 state 从新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和解决服务器数据的次要形式

setState() 视为 申请 而不是立刻更新组件的命令。为了更好的感知性能,React 会提早调用它,而后通过一次传递更新多个组件。React 并不会保障 state 的变更会立刻失效。

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

参数一能够有两种模式

1. 承受对象类型

setState(stateChange[, callback])

stateChange 会将传入的对象浅层合并到新的 state 中,例如 让 count +1

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

这种模式的 setState() 也是异步的,并且在同一周期内会对多个 setState 进行批处理。例如,如果在同一周期内屡次 count + 1,则相当于:

Object.assign(
  previousState, // 之前的状态
  {count: state.count + 1},
  {count: state.count + 1},
  ...
)

后调用的 setState() 将笼罩同一周期内先调用 setState 的值,因而商品数仅减少一次。如果后续状态取决于以后状态,咱们倡议应用 updater 函数 的模式代替:

this.setState((state) => {return { count: state.count + 1}
})

2. 承受函数参数

咱们在初始的 state 中并没有一个 count。然而当初咱们有一个需要:那就是增加一个 countstate 中,应用对象作为第一参数,你就会发现这样一个问题。

changeCount () {
    this.setState({count: 0}) // => this.state.count 是 undefined
    this.setState({count: this.state.count + 1}) // => undefined + 1 = NaN
    this.setState({count: this.state.count + 2}) // => NaN + 2 = NaN
}

下面的代码的运行后果并不能达到咱们的预期,咱们心愿 count 运行后果是 3,可是最初失去的是 NaN。然而这种后续操作依赖前一个 setState 的后果的状况并不常见。

这里就天然地引出了 setState 的第二种应用形式,能够承受一个函数作为参数。React.js 会把上一个 setState 的后果传入这个函数,你就能够应用该后果进行运算、操作,而后返回一个对象作为更新 state 的对象:

changeCount () {this.setState((prevState) => {return { count: 0}
    })
    this.setState((prevState) => {return { count: prevState.count + 1}
    // 上一个 setState 的返回是 count 为 0,这里须要执行 +1 所以以后返回 1
  })
    this.setState((prevState) => {return { count: prevState.count + 2}
    // 上一个 setState 的返回是 count 为 1,这里须要执行 +2 所以以后返回 3
    })
  // 最初的后果是 this.state.count 为 3
}

这样就能够达到上述的 利用上一次 setState 后果进行运算 的成果。

参数二为回调函数

setState() 的第二个参数为可选的回掉函数,它将在 setState 实现合并并从新渲染组件后执行。通常,咱们倡议应用 componentDidUpdate() 来代替此形式。

咱们来给案例中的 changeMessage 函数增加两个打印形式。

changeMessage() {
  this.setState({message: "Hello xhs,your message is changed."},() => {console.log('callback result:',this.state.message);
  })
  console.log('no callback result:',this.state.message);
}

咱们来看看后果

从图片中咱们能够看出

  • setState 内部的时候,并不能够立马拿到咱们想要的后果
  • callback 中则返回的是批改之后的后果。

正因为不是立马拿到咱们想要的后果,所以这就是咱们接下来要讲的 setState 可能是异步更新

这样咱们就能够晓得 setState 的第二参数的作用,就是能够确保失去 state 曾经批改之后的后果。也就是从新渲染之后执行的内容。

setState 可能是异步更新

咱们来看上面的代码:

  • 最终打印后果是 Hello React
  • 可见 setState 是异步的操作,咱们并不能在执行完 setState 之后立马拿到最新的 state 的后果
changeMessage() {
  this.setState({message: "Hello xhs,your message is changed."})
  console.log(this.state.message); // Hello React
}

为什么 setState 设计为异步呢?

  • setState 设计为异步其实之前在 GitHub 上也有很多的探讨;
  • React 核心成员(Redux 的作者)Dan Abramov 也有对应的回复,能够参考一下;

咱们来对他的答复做一个简略的总结:

  • setState 设计为异步,能够显著的晋升性能;
  • 如果每次调用 setState 都进行一次更新,那么意味着 render 函数会被频繁调用,界面从新渲染,这样效率是很低的;

留神: 最好的方法应该是获取到多个更新,之后进行批量更新;

  • 如果同步更新了 state,然而还没有执行 render 函数,那么 stateprops 不能放弃同步;

留神:stateprops 不能放弃一致性,会在开发中产生很多的问题;

获取到更新后的值

  • setState 的第二参数,一个回调函数,这个回调函数会在更新后会执行;
changeMessage() {
  this.setState({message: "Hello xhs,your message is changed."}, () => {console.log(this.state.message); // Hello xhs,your message is changed.
  });
}

当然,咱们也能够在生命周期函数:

componentDidUpdate(prevProps, provState, snapshot) {console.log(this.state.message);
}

setState 肯定是异步?

纳闷:setState肯定是异步更新的吗?

验证一:在 setTimeout 中的更新:

changeText() {setTimeout(() => {
    this.setState({message: "你好啊, 小和山"});
    console.log(this.state.message); // 你好啊, 小和山
  }, 0);
}

验证二:原生 DOM 事件:

componentDidMount() {const btnEl = document.getElementById("btn");
  btnEl.addEventListener('click', () => {
    this.setState({message: "你好啊, 小和山"});
    console.log(this.state.message); // 你好啊, 小和山
  })
}

其实分成两种状况:

  • 在组件生命周期或 React 合成事件中,setState 是异步;
  • setTimeout 或者原生 dom 事件中,setState 是同步;

setState 的合并

数据的合并

当你调用 setState() 的时候,React 会把你提供的对象合并到以后的 state

state 定义一些数据

this.state = {
  name: 'xhs',
  message: 'Hello React',
  count: 0,
}

通过 setState 去批改 state 中的 message,是不会对 name 产生影响的

changeMessage() {
  this.setState({message: "Hello xhs,your message is changed."});
}

多个 setState 会被合并

比方咱们还是有一个 count 属性,默认为 0,记录以后的数字:

  • 执行上面的操作之后,最初的答案是 1
  • 多个 state 进行了合并
increment() {
    this.setState({count: this.state.count + 1});

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

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

如何能够做到,让 count 最终变成 3 呢?

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

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

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

下面的代码就是用到了,setState 接管一个函数作为第一参数的状况,来解决这个问题,就能够很好的失去咱们期待的后果。

下节预报

本节咱们学习了 ReactSetState 其中的神秘,在下一个章节咱们将持续学习 React 中受控组件和非受控组件的内容,敬请期待!

正文完
 0