1.为什么使用setState
开发过程中我们不能直接通过修改state的值来让界面发生更新。
我们修改state之后,希望React根据最新的State来重新渲染界面,但是下面这种对state进行直接修改,React并不知道数据发生了变化。
所以,我们必须通过 setState 的方式来修改 state,以此来告知React此时数据已经发生改变。
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
}
render() {
return (
<div>
<h2>当前计数: {this.state.counter}</h2>
<button onClick={() => this.increment()}>+1</button>
</div>
)
}
increment() {
this.state.counter += 1; //直接修改state
console.log(this.state.counter)
}
}
上述中 increment() 修改如下:
increment() {
this.setState({
counter: this.state.counter + 1
})
}
setState方法是从Component中继承过来的。
2.setState异步更新
import React, { Component } from 'react';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
message: "Hello World"
}
}
render() {
return (
<div>
<h2>当前计数: {this.state.message}</h2>
<button onClick={e => this.changeText()}>改变文本</button>
</div>
)
}
changeText() {
// setState是异步更新
this.setState({
message: "你好,裹着新的光"
})
console.log(this.state.message); // 输出 Hello World
}
}
以上代码最终在控制台输出的是 Hello World。 这说明 setState 是异步的操作,并不是在执行完 setState 之后拿到 state 的最新结果。
2.1.为什么 setState 设计是异步的呢?
RFClarification: why is setState asynchronous? · Issue #11527 · facebook/react (github.com)
- setState设计为异步,可以显著提升性能。
-
- 如果每次调用setState都想进行一次更新,那么意味着 render 函数会被频繁调用,界面一次次的重新渲染,这样效率是很低的。
-
- 最好的方法就是获取多个更新后,之后进行批量更新。
- 如果同步更新了 state ,但是还没有执行 render 函数,那么state 和 props 不能保持同步。
-
- state 与 props 不能保持一致性,会在开发中产生很多问题。
import React, { Component } from 'react';
function Home(props) {
// Hello World
return <h1>{props.message}</h1>
}
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
message: "Hello World"
}
}
render() {
return (
<div>
<h2>当前文本: {this.state.message}</h2>
<button onClick={e => this.changeText()}>改变文本</button>
<Home message={this.state.message}/> //将message传给组件 Home
</div>
)
}
changeText() {
// 2.setState是异步更新
this.setState({
message: "你好,裹着新的光"
})
console.log(this.state.message); // Hello World
}
}
点击按钮前:
点击按钮后:
2.2.如何获取异步更新的结果
- 方式一:setState 回调函数
-
- setState接受两个参数:第二个参数是一个回调函数,这个回调函数会在更新后执行。
- 方式二:也可在生命周期函数中进行
componentDidUpdate(prevProps, prevState, snapshot)
import React, { Component } from 'react';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
message: "Hello World"
}
}
render() {
return (
<div>
<h2>当前文本: {this.state.message}</h2>
<button onClick={e => this.changeText()}>改变文本</button>
</div>
)
}
componentDidUpdate() {
// 方式二: 获取异步更新的state
console.log(this.state.message);
}
changeText() {
// 方式一: 获取异步更新后的数据
// setState(更新的state, 回调函数)
this.setState({
message: "你好啊,李银河"
}, () => {
console.log(this.state.message);
})
}
}
tips:根据源码,执行顺序是先执行 componentDidUpdate() ,再执行回调函数。两个函数都可获取更新后的结果。
3.setState是同步更新
3.1.情况一: 将setState放入到定时器中
changeText() {
// 情况一: 将setState放入到定时器中
setTimeout(() => {
this.setState({
message: "你好啊,李银河"
})
console.log(this.state.message);
}, 0);
}
3.2.情况二: 将setState放入到原生的DOM事件中
import React, { Component } from 'react';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
message: "Hello World"
}
}
render() {
return (
<div>
<h2>{this.state.message}</h2>
<button id="btn">改变文本</button>
</div>
)
}
componentDidMount() {
document.getElementById("btn").addEventListener("click", (e) => {
this.setState({
message: "你好啊,李银河"
})
console.log(this.state.message);
})
}
}
总结:
- 在组件生命周期或者React合成事件中,setState是异步;
- 在setTimeout或者原生DOM事件中,setState是同步;
4.setState数据的合并
import React, { Component } from 'react';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
message: "Hello World",
name: "zhLeon521"
}
}
render() {
return (
<div>
<h2>{this.state.message}</h2>
<h2>{this.state.name}</h2>
<button onClick={e => this.changeText()}>改变文本</button>
</div>
)
}
changeText() {
// 了解真相你才能获得真正的自由
this.setState({
message: "你好啊,裹着心的光"
});
// Object.assign({}, this.state, {message: "你好啊,裹着心的光"})
}
}
- 通过setState去修改message,是不会对name产生影响的;
- 源码中其实是有对 原对象 和 新对象进行合并的:
Object.assign({}, this.state, {message: "你好啊,裹着心的光"})
5.setState本身的合并
5.1.setState本身被合并
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
}
render() {
return (
<div>
<h2>当前计数: {this.state.counter}</h2>
<button onClick={e => this.increment()}>+1</button>
</div>
)
}
increment() {
// 1.setState本身被合并
this.setState({
counter: this.state.counter + 1
});
this.setState({
counter: this.state.counter + 1
});
this.setState({
counter: this.state.counter + 1
});
}
}
点击前:
点击后:
5.2.setState合并时进行累加
setState 可以传入一个函数,源码中有这个
increment() {
// 2.setState合并时进行累加
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
}
点击前:
点击后: