聊聊setState的用法,你会几个?

开发 前端
首先,这个问题的抛出,我会想为什么要抛出这个问题呢?如果说,你需要依赖状态更新后的值时,那么首先如何做呢?

[[387190]]

本文转载自微信公众号「前端UpUp」,作者前端UpUp。转载本文请联系前端UpUp公众号。

setState是同步还是异步?

首先,这个问题的抛出,我会想为什么要抛出这个问题呢?如果说,你需要依赖状态更新后的值时,那么首先如何做呢?

  • 对于Class Component而言,我们可以在componentDidMount或者是componentDidUpdate阶段来执行。
  • 对于Function Component而言,我们可以在useEffect的回调函数中执行。

首先,我们先给出结论,在React中不同的模式它的情况是不一样的,主要拿两种模式来说。

  1. legacy模式
  2. concurrent模式

legacy 模式

这是当前 React app 使用的方式??

  1. ReactDOM.render(<App />, rootNode) 

当前没有计划删除本模式,但是这个模式可能不支持这些新功能。

回到我们上述的问题,setState是同步的还是异步的?

  • 当在legacy模式下,命中batchedUpdates时,setState是异步的。
  • 当在legacy模式下,没命中batchedUpdates时,setState是同步的。

既然聊到了这里,我们来说一说batchedUpdates函数的作用。

那么它是干嘛的呢?如果你在处理逻辑函数中多次调用this.setState时,它是如何更新状态的呢?

  1. this.setState({ 
  2.   value: this.state.value + 1 
  3. }) 
  4. this.setState({ 
  5.   value: this.state.value + 1 
  6. }) 
  7. this.setState({ 
  8.   value: this.state.value + 1 
  9. }) 

那React实现了这个批量更新的操作,将多次的setState合并为一次更新,那么它是如何实现的呢?batchedUpdates函数就登场了。

  1. function batchedUpdates$1(fn, a) { 
  2.     var prevExecutionContext = executionContext; 
  3.     executionContext |= BatchedContext; 
  4.  
  5.     try { 
  6.       return fn(a); 
  7.     } finally { 
  8.       executionContext = prevExecutionContext; 
  9.  
  10.       if (executionContext === NoContext) { 
  11.         // Flush the immediate callbacks that were scheduled during this batch 
  12.         resetRenderTimer(); 
  13.         flushSyncCallbackQueue(); // 同步的更新 
  14.       } 
  15.     } 
  16.   } 

这个函数会传递一个fn,当执行fn之前,会在executionContext变量上附加一个BatchedContext,当fn执行完毕后,executionContext就会把之前的BatchedContext标记给去除掉。

  • 这样子一来,当executionContext带上了BatchedContext标记的话,react内部就会去做判断,带上了这个标记,这次的更新就是批处理,那么此次更新就是异步的。

那么,我们是不是能够假设一下,如果在执行完fn函数后,再去更新状态的话,是不是就能完成同步的更新呢?

setTimeout函数,我们可以把setState放在定时器中,这样子一来的话,当fn函数执行完时,BatchedContext标记也去掉了,然后等到 setTimeout 的回调函数等到空闲被执行的时候,才会执行 setState。

  1. setTimeout(() => { 
  2.     this.setState({ value: this.state.value + 1}) 
  3.   }, 0) 

这也就是当executionContext === NoContext,也就是会执行flushSyncCallbackQueue函数,完成此次的同步更新。

当然了,在concurrent 模式下,又是有所不同的。

这个时候,我们得谈一谈scheduleUpdateOnFiber函数。

我们都知道任务调度的起点是 scheduleUpdateOnFiber 方法,React.render、setState、forceUpdate、React Hooks 的dispatchAction 都会经过 scheduleUpdateOnFiber。

  1. function scheduleUpdateOnFiber(fiber, lane, eventTime) { 
  2.     // ... 
  3.     if (root === workInProgressRoot) { 
  4.       // ......  
  5.     } // TO an argument to that function and this one. 
  6.     if (lane === SyncLane) {  // 同步任务 
  7.       if ( // 检查当前是不是在unbatchedUpdates(非批量更新),(初次渲染的ReactDOM.render就是unbatchedUpdates) 
  8.       (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering 
  9.       (executionContext & (RenderContext | CommitContext)) === NoContext) { 
  10.         // Register pending interactions on the root to avoid losing traced interaction data. 
  11.         schedulePendingInteractions(root, lane);  
  12.         performSyncWorkOnRoot(root); 
  13.       } else { 
  14.         ensureRootIsScheduled(root, eventTime); 
  15.         schedulePendingInteractions(root, lane); 
  16.         if (executionContext === NoContext) { 
  17.           resetRenderTimer(); 
  18.           flushSyncCallbackQueue(); 
  19.         } 
  20.       } 
  21.     } else { // 异步任务 
  22.       // concurrent模式下是跳过了 flushSyncCallbackQueue 同步更新 
  23.       // .... 
  24.     }  
  25.   } 

scheduleUpdateOnFiber函数通过lane === SyncLane来判断是同步任务还是异步任务,我们通过ReactDom.render()方式创建的React app是会进入这个判断的,而concurrent模式下,则不同,那么它是如何创建的呢??

concurrent 模式

你可以理解成,这个暂时还是实验阶段,当未来稳定后,将会作为React开发的默认开发模式,它是如何创建一个React App应用的呢??

  1. ReactDOM.createRoot(rootNode).render(<App />) 

这个模式开启了所有的新功能。

concurrent模式下状态的更新都是异步的。

关于React的concurrent 模式解读,有兴趣可以看看官方文档。

到这里的话,似乎我们对React中setState是同步的还是异步的就有所了解了。

 

  • 哪些会命中batchUpdate机制
  • 生命周期(和它调用函数)
  • React中注册的事件
  • React可以'管理入口'

 

责任编辑:武晓燕 来源: 前端UpUp
相关推荐

2022-04-06 07:32:41

Java运算符变量

2022-06-04 08:29:27

awk命令BEGIN

2019-11-22 09:30:59

设计Java程序员

2020-04-07 08:51:25

CCNP协议网络协议路由

2022-03-01 07:52:38

链表指针节点

2023-09-13 08:00:57

云原生Java开发者

2022-03-03 07:34:31

注解容器作用域

2022-04-13 09:01:45

SASSCSS处理器

2021-11-29 11:11:45

SQL查询技巧

2024-04-26 08:10:49

2021-09-12 17:25:12

SQLite数据库

2021-08-16 08:12:04

SQLMerge用法

2021-03-02 07:33:13

开发C#字符

2022-06-07 23:28:05

线程安全后端

2021-06-05 23:39:52

c++函数场景

2021-09-08 18:42:29

setStateAPI函数

2018-02-25 10:45:08

Linux命令uptime

2019-04-15 13:24:31

2016-12-26 16:34:41

技术

2022-05-11 08:22:54

IO负载NFSOS
点赞
收藏

51CTO技术栈公众号