2023年11月26日发(作者:)
React18新特性(⼀):⾃动批量更新
前⾔
18 版本之前
经典⾯试题:setState 是同步还是异步
在 版本之前,在⾯试中经常会出现这个问题,那么答案⼜是什么样的呢?
react 18
在 合成事件中是异步的
React
在 中是异步的
hooks
其他情况皆是同步的,例如:原⽣事件、、 等
setTimeoutPromise
看看下⾯这段代码的执⾏结果,就知道所⾔⾮虚了
class App extends React.Component {
state = {
count: 0
}
componentDidMount() {
this.setState({count: this.state.count + 1})
console.log(this.state.count);
this.setState({count: this.state.count + 1})
console.log(this.state.count);
setTimeout(() => {
this.setState({count: this.state.count + 1})
console.log(this.state.count);
this.setState({count: this.state.count + 1})
console.log(this.state.count);
});
}
render() {
return <h1>Count: {this.state.count}h1>
}
}
有经验的同学肯定都知道,最终的结果是: 。
0 0 2 3
原因就是因为 中的 是批量更新,在整体逻辑没⾛完之前,不会进⾏更新。所以前两次打印结果都是 0,并且
componentDidMountsetState
将两次更新合并成了⼀次。
⽽在 中,脱离了 的掌控,变成了同步更新,因为下⽅的 可以实时打印出即时的状态。
setTimeoutReactlog
此时 的内部的处理逻辑我们可以写⼀段代码简单模拟⼀下:
React
先声明三个变量,⽤来记录数据
isBatchUpdate
: 判断是否批量更新的标志
count
: 状态
queue
: 存储状态的数组
声明⼀个 ⽅法,来模拟 合成事件
handleClickReact
声明⼀个 ⽅法,来模拟 的
setStateReactsetState
//
判断是否批量更新的标志
let isBatchUpdate = false;
//
状态
let count = 0;
//
存储最新状态的数组
let queue = [];
const setState = (state) => {
//
批量更新,则将状态暂存,否则直接更新
if (isBatchUpdate) {
queue.push(state);
} else {
count = state;
}
}
const handleClick = () => {
// isBatchUpdate true
进⼊事件,先将设置为
isBatchUpdate = true
setState(count + 1)
console.log(count);
setState(count + 1)
console.log(count);
setTimeout(() => {
setState(count + 1)
console.log(count);
setState(count + 1)
console.log(count);
})
// isBatchUpdate false
事件结束,将置为
isBatchUpdate = false;
}
handleClick();
count = queue.pop();
// queue
更新完成,重置状态数组
queue = [];
可以看到,上⾯这段代码的打印结果也是 。
0 0 2 3
⼿动批量更新
上⾯提到,在原⽣事件以及 等情况下, 是同步的,那如果我们仍然希望这种情况下可以同步更新,该怎么办呢?
setTimeoutsetState
ReactreactAPIunstable_batchedUpdates
也提供了⼀种解决⽅案:从 包中暴露了⼀个 :
那我们简单⽤⼀下看看效果:
class App extends React.Component {
state = {
count: 0
}
componentDidMount() {
this.setState({count: this.state.count + 1})
console.log(this.state.count);
this.setState({count: this.state.count + 1})
console.log(this.state.count)
setTimeout(() => {
React.unstable_batchedUpdates(() => {
this.setState({count: this.state.count + 1})
console.log(this.state.count)
this.setState({count: this.state.count + 1})
console.log(this.state.count)
})
})
}
render() {
return <h1>Count: {this.state.count}h1>
}
}
可以看到此时的打印结果为 。
0 0 1 1
Ok,React 18 之前 的更新⽅式就说到这⾥,那 React 18 ⾥做了什么改动呢?
setState
React 18 版本之后
上⾯提到了默认批量更新以及⼿动批量更新,那有些同学不满⾜了呀,觉得⼿动的还是不够智能,在很多情况下还得⼿动去调⽤
unstable_batchedUpdates
这个函数,⽤起来不爽。
别急,React 18 新版本就可以解决这些同学的痛点了!
Ok,直接上代码,看看 React 18 到底怎么⽤的
class App extends React.Component {
state = {
count: 0
}
componentDidMount() {
this.setState({count: this.state.count + 1})
console.log(this.state.count);
this.setState({count: this.state.count + 1})
console.log(this.state.count)
setTimeout(() => {
this.setState({count: this.state.count + 1})
console.log(this.state.count)
this.setState({count: this.state.count + 1})
console.log(this.state.count)
})
}
render() {
return <h1>Count: {this.state.count}h1>
}
}
// react 18 dom render
使⽤新的并发模式写法进⾏
ReactDOM.createRoot(document.getElementById('#root')!).render(<App />)
组件代码保持和第⼀版的⼀致,没有使⽤ 。
unstable_batchedUpdates
可以看到,此时的打印结果也是:
0 0 1 1
仅仅是使⽤了新的 : 。React 就能实现⾃动的批量更新了。感觉有点神奇。
APIRoot(root).render(jsx)
我们依然写⼀段代码来模拟⼀下这个过程:
此时不需要 来判断是否批量更新了,⽽是通过更新的优先级来进⾏判断
isBatchUpdate
每次更新会进⾏优先级的判定,相同优先级的任务会被合并。
事件执⾏完毕,进⾏任务的执⾏和更新
//
状态
let count = 0;
//
存储状态的数组
let queue = [];
const setState = (state) => {
const newState = {payload: state, priority: 0 }
//
判断当前优先级的任务集合是否存在,不存在则初始化,存在则存到对应由县级的任务集合中
if (queue[newState.priority]) {
queue[newState.priority].push(newState.payload)
} else {
queue[newState.priority] = [newState.payload]
}
}
const handleClick = () => {
setState(count + 1)
console.log(count);
setState(count + 1)
console.log(count);
setTimeout(() => {
setState(count + 1);
console.log(count);
setState(count + 1)
console.log(count);
})
}
handleClick();
count = queue.pop().pop();
setTimeout(() => {
count = queue.pop().pop();
})
可以看到,上⾯这段代码的执⾏结果也是
0 0 1 1
上述模拟代码仅为了展⽰优先级批量更新,不代表任何 React 源码的逻辑和思想
好了,⾃动批量更新的新特性就说到这⾥了。这⾥引⼊了三个问题:
1. Q: React 18 之后提供了 (root).render(jsx) 的 API,那之前 的 API 还⽀持吗?
Root
A: ⽀持的,并且⾏为和之前版本是⼀致的。只有使⽤了 这种⽅式,才会启⽤新的并发模式。
Root
2. Q: React 全⾃动更新后,那如果我就是想拿到更新之后的数据怎么办呢?
A: 类组件中可以使⽤ 的⽅式,在 中取到最新的值,函数组件可以使⽤ ,将 作为依
setState(state, callback)callbackuseEffectstate
赖。即可以拿到最新的值。
3. Q: ⽂章中说到的优先级的概念是怎么回事呢?
A: 这个涉及到 React 最新的调度以及更新的机制,优先级的概念以及其他优先级的任务如何创建,我们之后会⼀⼀展开来说。
⽬前的话,可以理解为 React 的更新机制进⾏了变化,不再依赖于批量更新的标志。⽽是根据任务优先级来进⾏更新:⾼优先级的任
务先执⾏,低优先级的任务后执⾏。
写在后⾯
代码量很少,主要是修改了 的渲染⽅式,可以亲⾃尝试⼀下,有疑惑的地⽅可以说出来⼀起进⾏讨论。
ReactDOM
如果有写的不对或不严谨的地⽅,欢迎⼤家能提出宝贵的意见,⼗分感谢。
如果喜欢或者有所帮助,欢迎 Star,对作者也是⼀种⿎励和⽀持。


发布评论