为什么使用setState
不能直接通过修改state的值来让界面发生更新:
-
在React中,每个组件都有自己的状态(state)和属性(props),当这些状态或属性发生变化时,React会重新渲染组件并更新UI。为了实现这一过程,React会先生成一个新的虚拟DOM树,然后将其与之前的虚拟DOM树进行比较,以确定需要更新哪些节点。最后,React将这些更新应用到真实的DOM树上,最终实现UI的更新。
-
如果我们直接修改
state的值而不是通过setState等API来更新组件状态,React就无法捕获状态变化,无法生成新的虚拟DOM树,也就无法进行比较和更新UI。这是React不能直接通过修改state来让界面更新的根本原因。 -
综上所述,React不能直接通过修改state来让界面更新的原因:是因为React采用了一种基于虚拟DOM的更新机制。通过使用
setState等API来更新组件状态,可以触发React的更新机制,最终实现UI的更新。
在组件中并没有实现setState的方法,为什么可以调用呢?
- setState方法是从Component中继承过来的
*使用
// 1.setState更多用法
// 1.基本使用
this.setState({
message: "你好啊, 李银河"
})
// 2.setState可以传入一个回调函数
// 好处一: 可以在回调函数中编写新的state的逻辑
// 好处二: 当前的回调函数会将之前的state和props传递进来
this.setState((state, props) => {
// 1.编写一些对新的state处理逻辑
// 2.可以获取之前的state和props值
console.log(this.state.message, this.props)
return {
message: "你好啊, 李银河"
}
})
// 3.setState在React的事件处理中是一个异步调用
// 如果希望在数据更新之后(数据合并), 获取到对应的结果执行一些逻辑代码
// 那么可以在setState中传入第二个参数: callback
this.setState({ message: "你好啊, 李银河" }, () => {
console.log("++++++:", this.state.message)
})
setState异步更新
setState的更新是异步的
- 可见setState是异步的操作,不能在执行完
setState之后立马拿到最新的state的结果
this.setState({ message: "你好啊, 李银河" }, () => {
console.log("++++++:", this.state.message)
})
// message: "Hello World"
console.log("------:", this.state.message)
为什么setState设计为异步呢?
- React核心成员(Redux的作者)Dan Abramov对应的回复: https://github.com/facebook/react/issues/11527#issuecomment-360199710
setState设计为异步,可以显著的提升性能;- 如果每次调用
setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,这样效率是很低的; - 最好的办法应该是获取到多个更新,之后进行批量更新;
- 如果每次调用
- 如果同步更新了
state,但是还没有执行render函数,那么render和props不能保持同步;- state和props不能保持一致性,会在开发中产生很多的问题;
如何获取异步的结果
一、setState 的回调
setState接受两个参数:第二个参数是一个回调函数,这个回调函数会在更新后会执行;- 格式如下:setState(partialState, callback)
this.setState({
message: "你好"
},() => {
console.log(this.state.message)
})
二、生命周期函数:componentDidUpdate
componentDidUpdate是React组件生命周期中的一个方法,用于在组件更新后执行一些操作。当组件的props或state发生变化时,React会调用componentDidUpdate方法,并传递三个参数:prevProps、prevState和snapshot。
prevProps:表示组件更新前的props值,是一个对象类型。可以通过比较当前props和prevProps的差异,来进行一些特定的操作。prevState:表示组件更新前的state值,是一个对象类型。可以通过比较当前state和prevState的差异,来进行一些特定的操作。snapshot:是一个可选参数,表示组件更新前的快照(即组件更新前的DOM状态)。通常在getSnapshotBeforeUpdate方法中返回一个快照值,在componentDidUpdate方法中使用快照值来进行一些特定的操作,例如恢复滚动位置等。
在componentDidUpdate方法中,我们可以访问组件的this.state属性,以获取更新后的state值。但需要注意的是,在componentDidUpdate方法中修改state可能会导致组件再次渲染,从而陷入无限循环的问题,因此应该谨慎地使用setState方法。
componentDidUpdate(prevProps,provState,snapshot){
console.log(this.state.message)
}
setState一定是异步吗?
setState的两种情况 (React18之前)
- 在组件生命周期或React合成事件中,setState是异步;
- 在setTimeout或者原生dom事件中,setState是同步;
setState默认是异步的(React18之后)
- 在React18之后,默认所有的操作都被放到了批处理中(异步处理)
- 希望代码可以同步会拿到,则需要执行特殊的flushSync操作:
changeText() {
setTimeout(() => {
// 在react18之前, setTimeout中setState操作, 是同步操作
// 在react18之后, setTimeout中setState异步操作(批处理)
flushSync(() => {
this.setState({ message: "你好啊, 李银河" })
})
console.log(this.state.message)
}, 0);
}
面试题:React的setState是同步的还是异步的?React18中是怎么样的?
-
在 React 中,可变状态通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新
-
React的setState是异步的 -- 不要指望在调用
setState之后,this.state会立即映射为新的值 -
在react18之前, 在setTimeout,Promise等中操作setState, 是同步操作
-
在react18之后, 在setTimeout,Promise等中操作setState,是异步操作(批处理)
- 如果需要同步的处理怎么办呢? 需要执行特殊的
flushSync操作
- 如果需要同步的处理怎么办呢? 需要执行特殊的
-
为什么要将setState设计成异步的
- 首先,若是将setState设计成同步的,在
componentDidMount中请求多个网络请求时,会堵塞后面的网络请求
componentDidMount() { // 网络请求一 : this.setState // 网络请求二 : this.setState // 网络请求三 : this.setState // 如果this.setState设计成同步的,会堵塞后面的网络请求 }-
一. setState设计为异步,可以显著的提升性能
- 如果每次调用 setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,这样效率是很低的
- 最好的办法应该是获取到多个更新,之后进行批量更新
// 在一个函数中有多个setState时, this.setState({}) --> 先不会更新,而是会加入到队列(queue)中 (先进先出) this.setState({}) --> 也加入到队列中 this.setState({}) --> 也加入到队列中 // 这里的三个setState会被合并到队列中去 // 在源码内部是通过do...while从队列中取出依次执行的 -
二: 如果同步更新了state,但是还没有执行render函数,那么state和props不能保持同步
- 首先,若是将setState设计成同步的,在