react
中没有对数据进行劫持,所以要通过setState
显式的改变数据,从而使render
函数重新执行,在react18
以前该方法在某些情况下(非react
处理:如setTimeout
或原生事件)是同步的,而在react18
以后都是异步了,如果想要同步需要使用flushSync
。在这之前先看看基本用法吧。
基本使用
- 直接传入一个新的
state
import React, { Component } from "react"
import { flushSync } from "react-dom"
class App extends Component {
constructor() {
super()
this.state = {
mes: "App",
counter: 0,
}
}
increament() {
this.setState({
counter: this.state.counter + 1,
})
}
render() {
return (
<div>
<h1>{this.state.mes}</h1>
<h3>{this.state.counter}</h3>
<button onClick={() => this.increament()}>+1</button>
<hr />
</div>
)
}
}
setState
是通过Object.assign
,来修改原来的对象,所以mes
依然存在
- 传入一个回调,接受原来的 state 与 props,返回值作为新的 state
increament() {
this.setState((state, props) => {
return { counter: state.counter + 1 }
})
}
- 接受一个回调作为第二个参数,该回调中的
state
是修改之后的
increament() {
this.setState({ counter: this.state.counter + 1 }, () => {
console.log("回调", this.state.counter)
})
console.log("外部", this.state.counter)
}
由此也可以看出
setState
是一个异步函数
传入回调与传入一个对象的区别
传递一个函数可以让你在函数内访问到当前的 state 的值,而传入对象是从 this.state
中读取数据的,但是 React 不会更新 this.state
,直到该组件被重新渲染。所以如果依赖于前一个state
就可以传入一个函数来修改,不过无论是哪种,render
都只会重新调用一次
在 React 中,this.props 和 this.state 都代表着已经被渲染了的值,即当前屏幕上显示的值
// 传入对象
increament() {
this.setState({ counter: this.state.counter + 1 })
this.setState({ counter: this.state.counter + 1 })
this.setState({ counter: this.state.counter + 1 })
}
// 当我们点击按钮是也只会增加 1
// 传入函数,并使用传入的参数
increament() {
this.setState(
(state, props) => {
return { counter: state.counter + 1 }
}
)
this.setState(
(state, props) => {
return { counter: state.counter + 1 }
}
)
this.setState(
(state, props) => {
return { counter: state.counter + 1 }
}
)
}
// 3
为什么是异步?
- 提升性能:如果多次调用了 setState,而是同步的话,可能会多次渲染
increament() {
// 将 setState 同步化模拟一下
flushSync(() => {
this.setState({
counter: this.state.counter + 1,
})
})
flushSync(() => {
this.setState({
counter: this.state.counter + 1,
})
})
flushSync(() => {
this.setState({
counter: this.state.counter + 1,
})
})
}
render 函数调用三次
- state与props更新不同步