"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至咱们官网小和山的菜鸟们 ( xhs-rookies.com/ ) 进行学习,及时获取最新文章。前端
"Code tailor" ,若是您对咱们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与咱们取的联系,您也能够在微信上观看咱们的文章。每个建议或是赞同都是对咱们极大的鼓励!react
这节咱们将介绍 React
中 setState
,但愿能够帮助你们真正理解 setState
。git
本文会向你介绍如下内容:github
setState
State
setState()
setState
多是异步更新setState
的合并在介绍 setState
以前,咱们先来看一个 setState
的案例,了解一下是如何使用的。web
咱们来展现一个使用案例,当点击一个 改变文本
的按钮时,修改界面前显示的内容:服务器
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
函数,页面就会被从新渲染。微信
点击按钮以后,从新渲染的效果:markdown
将上面的 changeMessage
方法,改为下面的样子。dom
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(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
。可是如今咱们有一个需求:那就是添加一个 count
在 state
中,使用对象做为第一参数,你就会发现这样一个问题。
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
已经修改以后的结果。也就是从新渲染以后执行的内容。
咱们来看下面的代码:
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
上也有不少的讨论;咱们来对他的回答作一个简单的总结:
setState
设计为异步,能够显著的提高性能;
若是每次调用 setState
都进行一次更新,那么意味着 render
函数会被频繁调用,界面从新渲染,这样效率是很低的;
注意: 最好的办法应该是获取到多个更新,以后进行批量更新;
state
,可是尚未执行 render
函数,那么 state
和 props
不能保持同步;注意:
state
和props
不能保持一致性,会在开发中产生不少的问题;
获取到更新后的值
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()
的时候,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."
});
}
复制代码
好比咱们仍是有一个 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
接收一个函数做为第一参数的状况,来解决这个问题,就能够很好的获得咱们期待的结果。
本节咱们学习了 React
中 SetState
其中的奥秘,在下一个章节咱们将继续学习 React
中受控组件和非受控组件的内容,敬请期待!