React: setState进阶

setState的使用

this.setState([partialState], [callback])
[partialState]: 支持部分状态更改
this.setState({
x: 100 // 不论总共有多少状态,我们只修改了x,其余的状态不懂
})
[callback]: 在状态更改,视图更新完毕后触发执行
发生在componentDidUpdate周期函数之后,(DidUpdate会在任何状态更改后都触发执行,而回调函数的方式,可以在指定状态更新后处理一些事情)
特殊: 即便我们基于shouldComponentUpdate组织了状态/视图的更新,DidUpdate周期函数肯定不会执行了,但是我们设置的这个callback回调函数依然会被触发执行!!
类似于Vue中的 nextTick!!

import React from 'react'

class Demo extends React.Component {
	state = {
		x: 10,
		y: 5,
		z: 0
	}
	handle = () => {
		let { x, y, z } = this.state;
		// 同时修改三个状态值,只会触发一次视图更新
		this.setState ({
			x:  x + 1,
			y:  y + 1,
			z: z + 1
		})
	}
	render () {
		console.log('视图渲染:RENDER');
		let { x, y ,z } = this.state;
		return  <div>
			x: {x} - y: {y} - z: {z}
			<br />
			<button onClick = {this.handle}> 按钮 </button>
		</div>
	}
}

而若是我们分别来修改状态值,那这样的操作是同步还是异步的呢?

this.setState({ x: x + 1 })
this.setState({ y: y + 1 })
this.setState({ z: z + 1 })

在这里插入图片描述
若是同步操作,那么每次setState都将触发shouldUpdate -> willUpdate 修改状态值 -> render -> didUpdate -> callback,视图将会更新三次,每次都将根据最新状态生成全新的virtualDOM,再去diff打补丁…
在React18中,setState在任何地方执行,都是“异步操作”
在React18中,有一套更新队列的机制
基于异步操作,实现状态的“批处理”
好处
减少视图更新的次数,降低渲染消耗的性能
让更新的逻辑和流程更清晰稳健

**更新队列updater:**
		- 在产生的私有上下文中,代码自上而下执行
		- 1. 会把所有的setState操作,先加入到更新队列(只对当前上下文,同步要做的事情做处理)
		- 2. 当上下文中的代码都处理完毕后,会让更新队列中的任务,统一渲染 / 更新一次 (批处理)

在这里插入图片描述
在React18中,setState操作都是异步的,不论是在哪执行,例如:合成事件、周期函数、定时器…
目的:实现状态的批处理(统一处理)
- 有效减少更新次数,降低性能消耗
- 有效管理代码执行的逻辑顺序
原理:利用了更新队列 updater 机制来处理的
- 在当前相同的时间段内 (浏览器此时可以处理的事情中,遇到setState会立即放入到更新队列中!)
- 此时状态 / 视图还未更新
- 当所有的代码操作结束,会“刷新队列” (通知更新队列中的任务执行):把所有放入的setState 合并在一起,执行一次视图更新 (批处理操作)

在React18 和 React16中,关于setState是同步还是异步,是有一些区别的!
React18中:不论在什么地方执行setState,它都是异步的(都是基于updater更新队列机制,实现的批处理)
React16中:如果在合成事件(jsx元素中基于onXxx绑定的事件)、周期函数中,setState的操作是异步的!!但是如果setState出现在其他异步操作中(例如:定时器、手动获取DOM元素做的事件绑定等),它将变为同步的操作(立即更新状态和让视图渲染)!!

// React16
handle = () => {
	let { x, y, z } = this.state;
	this.setState({ x: x + 1 }) // 异步
	this.setState({ y: y + 1 }) // 异步
	console.log(this.state) // {x: 10, y: 5, z: 0} -> 渲染

	setTimeOut(() => {
		this.setState( {z: z + 1} ) // 同步
		console.log(this.state) // 渲染 -> {x:  11, y: 6, z: 1}
	}, 1000)
}
// React18
handle = () => {
	let { x, y, z } = this.state;
	this.setState({ x: x + 1 }) // 异步
	this.setState({ y: y + 1 }) // 异步
	console.log(this.state) // {x: 10, y: 5, z: 0} -> 渲染

	setTimeOut(() => {
		this.setState( {z: z + 1} ) // 异步
		console.log(this.state) // 渲染 -> {x:  11, y: 6, z: 0} -> 渲染
	}, 1000)
}

若是开发中遇到需要几个状态修改之后,再去修改其他状态,此时可以通过 flushSync 实现:
flushSync :可以刷新 “updater更新队列”,也就是让修改状态的任务立即批处理一次!!
在这里插入图片描述

import React from 'react';
import { flushSync } from 'react-dom'

/*
	this.setState((prevState) => {
		// prevState:存储之前的状态值
		// return的对象,就是我们想要修改的新状态值(支持修改部分状态)
	})
*/
class Demo extends React.Component {
	state = {
		x: 0
	};
	handle = () => {
		for (let i = 0; i < 20; i++) {
			/* this.setState({
				x: this.state.x + 1
			}) */
			
			this.setState(prevStaee => {
				return {
					x: prevState.x + 1
				}
			});
		}

		render() {
			let { x } = this.state;
			return <div>
					x: {x}
					<br />
					<button onClick={this.handle}>按钮</button>
				</div>
		}
	}
}

export default Demo;

注意:setState写成函数的区别!!
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值