Code Monkey home page Code Monkey logo

Comments (31)

azl397985856 avatar azl397985856 commented on May 14, 2024 195

这里所说的同步异步, 并不是真正的同步异步, 它还是同步执行的。

这里的异步指的是多个state会合成到一起进行批量更新。

希望初学者不要被误导

from daily-interview-question.

7kms avatar 7kms commented on May 14, 2024 56

setState是同步还是异步?

基于当前最新稳定版本v16.14.0进行分析

此处同步异步的定义

我们知道Promise.then(),setTimeout是异步执行. 从js执行来说, setState肯定是同步执行.

所以这里讨论的同步和异步并不是指setState是否异步执行, 而是指调用setState之后this.state能否立即更新.

分析

  1. 众所周知调用setState({item: 'new xxx'})之后, 会将传入setState的参数包装成一个update对象并添加到updateQueue队列中.
  2. 之后updateQueue队列在什么时机被合并到this.state中才是本题目的关键. 因为合并之后this.state必然就已经更新了.
  3. state的合并是在fiber构建循环中进行的, 而fiber构建循环必然是在触发scheduler调度之后进行. 关于这一点的详细论述可以参考react两大工作循环.

到这里问题转化为调用setState之后, 是否立即触发scheduler调度?

  • 如果立即进行scheduler调度, 那么this.state必然能同步获取.
  • 反之, 如果异步进行scheduler调度, 那么this.state不能同步获取.
  1. 每次调用setState都会进行一次scheduler调度(可以参考React 调度机制).
    在最新源码v16.14.0中体现为调用ensureRootIsScheduled. 在该源码中, 可以得到回答本题目的最佳答案, 核心逻辑如下:
 if (
      (executionContext & LegacyUnbatchedContext) !== NoContext &&
      (executionContext & (RenderContext | CommitContext)) === NoContext
    ) {
       // .... 省略部分本次讨论不会涉及的代码
    } else {
      ensureRootIsScheduled(root, eventTime); // 触发scheduler调度(调度是异步的) , 所以该函数不会立即触发render.
      if (executionContext === NoContext) {  // 当执行上下文为0时, 会刷新同步队列
         // .... 省略部分本次讨论不会涉及的代码

        // 这里是关键,  执行同步回调队列. 有兴趣的同学可以继续在源码中查看, 可以得到结论:
        // if分支之外的ensureRootIsScheduled(root, eventTime)和此处的flushSyncCallbackQueue()
        // 最终都是调用performSyncWorkOnRoot进行fiber树的循环构建
        flushSyncCallbackQueue(); 
      }
    }

结论

  1. 如楼上 @taichiyi 所述, setState是同步和异步最关键的因素是react内部的执行上下文executionContext的状态.
  • executionContext为空时, 表现为同步.
  • 反之executionContext不为空, 表现为异步.
  1. executionContext何时为空?

这个问题反过来更好理解, 什么时候executionContext不为空? 因为executionContext是react内部控制的属性, 当初次render, 合成事件触发时都会改变executionContext的值.

  1. 只要绕开react内部触发更改executionContext的逻辑, 就能保证executionContext为空, 进而实现setState为同步.
  • 可以使用异步调用如setTimeout, Promise, MessageChannel等
  • 可以监听原生事件, 注意不是合成事件(合成事件是react体系, 会更改executionContext), 在原生事件的回调函数中执行 setState 就是同步的

附加条件

以上分析都是基于legacy模式进行分析的, 众所周知react即将(可能)全面进入concurrent模式(可以参考react 启动模式). 在concurrent模式下, 这个题目可能就没有意义了, 因为从目前最新代码来看, 在concurrent模式下根本就不会判断executionContext, 所以concurrent模式下setState都为异步.

 // concurrent模式下根本没有下列代码, 所以不可能同步
if (executionContext === NoContext) { 
        flushSyncCallbackQueue(); 
      }

演示示例

对于此问题在codesandbox上面做了一个demo(详细演示示例). 在legacyconcurrent模式下演示并验证了上述结论.

from daily-interview-question.

stevekeol avatar stevekeol commented on May 14, 2024 48

React的setState本身并不是异步的,是因为其批处理机制给人一种异步的假象。

【React的更新机制】

生命周期函数和合成事件中:

  1. 无论调用多少次setState,都不会立即执行更新。而是将要更新的state存入'_pendingStateQuene',将要更新的组件存入'dirtyComponent';
  2. 当根组件didMount后,批处理机制更新为false。此时再取出'_pendingStateQuene'和'dirtyComponent'中的state和组件进行合并更新;

原生事件和异步代码中:

  1. 原生事件不会触发react的批处理机制,因而调用setState会直接更新;
  2. 异步代码中调用setState,由于js的异步处理机制,异步代码会暂存,等待同步代码执行完毕再执行,此时react的批处理机制已经结束,因而直接更新。

总结:
react会表现出同步和异步的现象,但本质上是同步的,是其批处理机制造成了一种异步的假象。(其实完全可以在开发过程中,在合成事件和生命周期函数里,完全可以将其视为异步)

from daily-interview-question.

adminparry avatar adminparry commented on May 14, 2024 21

我的个人观点是这样的对于同步或者是异步都可以进行控制的想要同步就同步想要异步就异步
我老婆提醒我如果面试官问什么你管不了 谁让你想去那工作呢

from daily-interview-question.

dongkeng001 avatar dongkeng001 commented on May 14, 2024 11

由React控制的事件处理程序,以及生命周期函数调用setState不会同步更新state 。
React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval等。

from daily-interview-question.

minhuaF avatar minhuaF commented on May 14, 2024 8

react 18 发布之后,估计要对不同版本不同模式进行不同的回答了

from daily-interview-question.

qiruohan avatar qiruohan commented on May 14, 2024 7

异步更新,同步执行

from daily-interview-question.

Yuweiai avatar Yuweiai commented on May 14, 2024 5

(微医)React 中 setState 什么时候是同步的,什么时候是异步的?

【高能预警:你懂得越多,你不懂得更多】

  1. 4. 基础篇-玄学state:在不同的执行环境下,或者不同的 React 模式下,State 更新流程都是不同的

  2. React 是有多种模式的,基本平时用的都是 legacy 模式

    • 除了 legacy 模式,还有 blocking 模式和 concurrent 模式 —— blocking 可以视为 concurrent 的优雅降级版本和过渡版本,React 最终目的,不久的将来将以 concurrent 模式作为默认版本,这个模式下会开启一些新功能
    • React V18 版本,concurrent 将作为一个稳定的功能出现

1. legacy 模式下的 setState

1.1 React 在触发 setState 时主要做了哪些事:

  • 首先,setState 会产生当前更新的优先级(老版本用 expirationTime,新版本用 lane)
  • 接下来 React 会从 fiber Root 根部 fiber 向下调和子节点,调和阶段将对比发生更新的地方,更新对比 expirationTime,找到发生更新的组件,合并 state,然后触发 render 函数,得到新的 UI 视图层,完成 render
  • 接下来来到 commit 阶段,替换真实 DOM,完成此次更新流程,最后会执行 setState 中 callback 函数,到此为止完成了一次 setState 全过程

一个主要任务的先后顺序:render 阶段 render 函数执行 -> commit 阶段真实 DOM 替换 -> setState 回调函数执行 callback

1.2 类组件 setState 原理揭秘:

  • 类组件初始化过程中绑定了负责更新的 Updater 对象,调用 setState,实际上是 React 底层调用 Updater 对象上的 enqueueSetState 方法

    // react-reconciler/src/ReactFiberClassComponent.js
    
    enqueueSetState(){
         /* 每一次调用`setState`,react 都会创建一个 update 里面保存了 */
         const update = createUpdate(expirationTime, suspenseConfig);
         /* callback 可以理解为 setState 回调函数,第二个参数 */
         callback && (update.callback = callback) 
         /* enqueueUpdate 把当前的update 传入当前fiber,待更新队列中 */
         enqueueUpdate(fiber, update); 
         /* 开始调度更新 */
         scheduleUpdateOnFiber(fiber, expirationTime);
    }

    enqueueSetState 会创建一个 update,然后放入当前 fiber 对象的待更新队列中,最后开启调度更新,进入到(批量)更新流程

  • React 批量更新:

    // react-dom/src/events/DOMLegacyEventPluginSystem.js
    
    /* 在`legacy`模式下,所有的事件都将经过此函数同一处理 */
    function dispatchEventForLegacyPluginEventSystem(){
        // handleTopLevel 事件处理函数
        batchedEventUpdates(handleTopLevel, bookKeeping);
    }
    // react-dom/src/events/ReactDOMUpdateBatching.js
    
    function batchedEventUpdates(fn,a){
      /* 开启批量更新  */
      isBatchingEventUpdates = true;
      try {
        /* 这里执行了的事件处理函数, 比如在一次点击事件中触发setState,那么它将在这个函数内执行 */
        return batchedEventUpdatesImpl(fn, a, b);
      } finally {
        /* try 里面 return 不会影响 finally 执行  */
        /* 完成一次事件,批量更新  */
        isBatchingEventUpdates = false;
      }
    }
    • 在 React 事件执行之前通过 isBatchingEventUpdates = true打开开关,开启事件批量更新,当该事件结束,再通过isBatchingEventUpdates = false关闭开关,然后在·scheduleUpdateOnFiber `中根据这个开关来确定是否进行批量更新
    export default class index extends React.Component{
        state = { number:0 }
        handleClick= () => {
              this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback1', this.state.number)  })
              console.log(this.state.number)
              this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback2', this.state.number)  })
              console.log(this.state.number)
              this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback3', this.state.number)  })
              console.log(this.state.number)
        }
        render(){
            return <div>
                { this.state.number }
                <button onClick={ this.handleClick }  >number++</button>
            </div>
        }
    }

    0, 0, 0, callback1 1 ,callback2 1 ,callback3 1

  • 异步操作(promise 或者 setTimeout)打破批量更新规则:

    • setTimeout 中的 setState 为什么会多次 render?
      • 因为异步出了事件系统的执行上下文:触发时 isBatchingUpdates 为 false,所以多次 setState 没有被合并处理,能够直接进行更新
    setTimeout(()=>{
        this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback1', this.state.number)  })
        console.log(this.state.number)
        this.setState({ number:this.state.number + 1 },()=>{    console.log( 'callback2', this.state.number)  })
        console.log(this.state.number)
        this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback3', this.state.number)  })
        console.log(this.state.number)
    })

    callback1 1 , 1, callback2 2 , 2,callback3 3 , 3

  • 异步环境继续开启批量更新模式:

    React-Dom 中提供了批量更新方法 unstable_batchedUpdates,可以去手动批量更新

    import ReactDOM from 'react-dom'
    const { unstable_batchedUpdates } = ReactDOM
    
    setTimeout(()=>{
        unstable_batchedUpdates(()=>{
            this.setState({ number:this.state.number + 1 })
            console.log(this.state.number)
            this.setState({ number:this.state.number + 1})
            console.log(this.state.number)
            this.setState({ number:this.state.number + 1 })
            console.log(this.state.number) 
        })
    })

    0 , 0 , 0 , callback1 1 , callback2 1 ,callback3 1

    在实际工作中,unstable_batchedUpdates可以用于 Ajax 数据交互之后,合并多次 setState,或者时多次 useState —— 如果批量更新处理,一次数据交互多次改变 state 会促使视图多次渲染

  • 提升更新优先级:

    React-dom 提供了 flushSync ,flushSync 可以将回调函数中的更新任务,放在一个较高的优先级中

    handerClick=()=>{
        setTimeout(()=>{
            this.setState({ number: 1  })
        })
        
        this.setState({ number: 2  })
        
        ReactDOM.flushSync(()=>{
            this.setState({ number: 3  })
        })
        
        this.setState({ number: 4  })
    }
    
    render(){
       console.log(this.state.number)
       return ...
    }

    3 4 1

    首先 flushSync this.setState({ number: 3 })设定了一个高优先级的更新,然后 2 和 3 被批量更新到 3,最后更新 setTimeout 中的 1

    flushSync 中的 setState > 正常执行上下文中的 setState > setTimeout、Promise 中的 setState

1.3 函数组件 useState 原理揭秘

(未完待下一道面试题)18. 原理篇-hooks原理

2. V18concurrent模式下的 setState

(未完待下一道面试题)34. v18特性篇-concurrent 下的 state更新流程

from daily-interview-question.

taichiyi avatar taichiyi commented on May 14, 2024 3

先说结论:

如果是通过 DOM 事件(例如:onClick) 的事件处理函数调用了 setState 函数,导致 React 进行更新(或者说“调度”scheduleWork),则此时事件处理函数中的 setState 的行为是异步的(也就是合并更新)。

上面的评论中说了很多 setState “异步”的情况。我这里补充一下“同步”的情况,下面的例子使用了 setTimeOut,组件最后显示结果是33

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
    this.handleAddCount = this.handleAddCount.bind(this);
  }
  componentDidMount() {
    setTimeout(this.handleAddCount, 1*1000);
  }
  handleAddCount() {
    this.setState({
      count: this.state.count + 11
    });
    this.setState({
      count: this.state.count + 22
    });
  }
  render() {
    return <div>{this.state.count}</div>;
  }
}

源码:

从源码的角度来说,setState 的行为是“异步”还是“同步”取决于 React 执行 setState 方法时的执行上下文①(ExecutionContext)。

如果 ExecutionContext 为 0,表示当前没有正在进行的其他任务,则 setState 是“同步”的。React 源码地址:https://github.com/facebook/react/blob/b53ea6ca05d2ccb9950b40b33f74dfee0421d872/packages/react-reconciler/src/ReactFiberWorkLoop.js#L411

①注意这里的执行上下文不是浏览器的,为了更好的控制渲染任务,避免长时间占用浏览器的主线程, React 实现了自己的执行上下文

from daily-interview-question.

CC712 avatar CC712 commented on May 14, 2024 2

React Dan Abramov 大佬的解释
这题应该是想问 setStatereconiliation 特性

from daily-interview-question.

janyin avatar janyin commented on May 14, 2024 2

这个issue之前是没问题的。React18后setState全部都是异步的,所以不需要再分情况讨论,包括原生DOM事件和setTimeout之类的。

另外我觉得讨论这个问题没多大意义,因为setState本身设计出来就是异步的,要同步的访问state就用第二个参数回调访问啊。不然别人设计出第二个参数干嘛。

  • 面试应该问:为什么setState是异步的?
  • 而不是问:什么情况是同步,什么情况是异步?

参考: facebook/react#11527

from daily-interview-question.

vkboo avatar vkboo commented on May 14, 2024 1

setState是异步的,setState之后,取state中的值并不一定是最新更新的值。
这样设计的原因是可能在同步的代码中可能存在连续的多个setSate操作,react会对他们进行智能的合并,直到执行到了最后一个setState,React才回智能的合并state的,并异步的设置state的值,后续判断是否进行render操作.如果同步的每次setState都去走一遍reconciler & commit操作,则太耗性能了

from daily-interview-question.

Verten avatar Verten commented on May 14, 2024 1

在最新的版本里面,React已经默认auto batch updating了

reactwg/react-18#21

from daily-interview-question.

littleee avatar littleee commented on May 14, 2024 1

在最新的版本里面,React已经默认auto batch updating了

reactwg/react-18#21

针对这个问题,大家默认讨论版本是react15, fiber之前

from daily-interview-question.

Frank-MJ avatar Frank-MJ commented on May 14, 2024 1

如果setState 在React 能够控制的范围被调用,它就是异步的。 例如:合成事件处理函数, 生命周期函数, 此时会进行批量更新, 也就是将状态合并后再进行DOM 更新。 如果setState 在原生JavaScript 控制的范围被调用,它就是同步的

from daily-interview-question.

azl397985856 avatar azl397985856 commented on May 14, 2024

这里所说的同步异步, 并不是真正的同步异步, 它还是同步执行的。
这里的异步指的是多个state会合成到一起进行批量更新。
希望初学者不要被误导

你的说法还是太片面了,可以看下官网的介绍

image
https://reactjs.org/docs/faq-state.html#when-is-setstate-asynchronous

你是想说明什么,没太明白。

你的意思难道是从你截图给的这个可以看出reactsetState是或者部分是异步的?

---补充---

或者你的意思难道是标题是《何时setState是异步的》, 就断定 “setState确实可能是异步的”

from daily-interview-question.

xueqingxiao avatar xueqingxiao commented on May 14, 2024

这道题和19题有些类似。#18 (comment)

from daily-interview-question.

Lizhooh avatar Lizhooh commented on May 14, 2024

我们平时写的时候就当它是异步进行就好了。

from daily-interview-question.

ggzcg avatar ggzcg commented on May 14, 2024

@yygmind 被这问题吓到了,还好点进来看了,你做出了我放心的解释

from daily-interview-question.

tenadolanter avatar tenadolanter commented on May 14, 2024

单独的说同步异步没有意义,一般配合场景:setState后取值使用的,初学者经常发现setState后值还是旧的,而且这个错误很难发现。

from daily-interview-question.

yoghurtxu avatar yoghurtxu commented on May 14, 2024

我们知道Promise.then(),setTimeout是异步执行. 从js执行来说, setState肯定是同步执行.

所以这里讨论的同步和异步并不是指setState是否异步执行, 而是指调用setState之后this.state能否立即更新.

from daily-interview-question.

yqz0203 avatar yqz0203 commented on May 14, 2024

为什么setTimeout里面的setState就不能进行批处理?如果批处理会有什么异常,望解答。

from daily-interview-question.

littleee avatar littleee commented on May 14, 2024
isBatchingUpdates = true

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

isBatchingUpdates = false

看了这段应该就清晰很多了。 react 会在执行前加一个“锁”来标记是否需要批处理, 如上,react加了锁之后立刻就释放了,然后才会执行setTimeout里的setState, 也就是说setTimeout和原生事件会脱离react的控制。 只有在react控制下才会存在批处理,setState才会有“异步”效果。

为什么setTimeout里面的setState就不能进行批处理?如果批处理会有什么异常,望解答。

from daily-interview-question.

yqz0203 avatar yqz0203 commented on May 14, 2024
isBatchingUpdates = true

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

isBatchingUpdates = false

看了这段应该就清晰很多了。 react 会在执行前加一个“锁”来标记是否需要批处理, 如上,react加了锁之后立刻就释放了,然后才会执行setTimeout里的setState, 也就是说setTimeout和原生事件会脱离react的控制。 只有在react控制下才会存在批处理,setState才会有“异步”效果。

为什么setTimeout里面的setState就不能进行批处理?如果批处理会有什么异常,望解答。

这里的 “控制” 是指什么呢?如果脱离控制后会发生什么呢?

from daily-interview-question.

janyin avatar janyin commented on May 14, 2024

setTimeout/setInterval调用并不是同步setState,也是异步的

在线例子:https://stackblitz.com/edit/react-ts-sqs8mw?file=App.tsx

from daily-interview-question.

janyin avatar janyin commented on May 14, 2024

image

这段代码里输出的a是0,而不是3. 证明在setimeout里setState也是异步的

from daily-interview-question.

Akarinx avatar Akarinx commented on May 14, 2024

@janyin clickHandler 是通过 react 合成事件调用的呀

from daily-interview-question.

janyin avatar janyin commented on May 14, 2024

Akarinx

是合成事件,但是里面用的是setTimeout

from daily-interview-question.

janyin avatar janyin commented on May 14, 2024

这个是原生事件的例子: https://stackblitz.com/edit/react-ts-kabj1e?file=index.tsx,App.tsx
image

@janyin clickHandler 是通过 react 合成事件调用的呀

from daily-interview-question.

Akarinx avatar Akarinx commented on May 14, 2024

这个是原生事件的例子: https://stackblitz.com/edit/react-ts-kabj1e?file=index.tsx,App.tsx image

@janyin clickHandler 是通过 react 合成事件调用的呀

噢噢 是我这边的问题,确实是因为 react18 的问题

from daily-interview-question.

MiJiaCoding avatar MiJiaCoding commented on May 14, 2024

(微医)React 中 setState 什么时候是同步的,什么时候是异步的?

【高能预警:你懂得越多,你不懂得更多】

  1. 4. 基础篇-玄学state:在不同的执行环境下,或者不同的 React 模式下,State 更新流程都是不同的
  2. React 是有多种模式的,基本平时用的都是 legacy 模式
    • 除了 legacy 模式,还有 blocking 模式和 concurrent 模式 —— blocking 可以视为 concurrent 的优雅降级版本和过渡版本,React 最终目的,不久的将来将以 concurrent 模式作为默认版本,这个模式下会开启一些新功能
    • React V18 版本,concurrent 将作为一个稳定的功能出现

1. legacy 模式下的 setState

1.1 React 在触发 setState 时主要做了哪些事:

  • 首先,setState 会产生当前更新的优先级(老版本用 expirationTime,新版本用 lane)
  • 接下来 React 会从 fiber Root 根部 fiber 向下调和子节点,调和阶段将对比发生更新的地方,更新对比 expirationTime,找到发生更新的组件,合并 state,然后触发 render 函数,得到新的 UI 视图层,完成 render
  • 接下来来到 commit 阶段,替换真实 DOM,完成此次更新流程,最后会执行 setState 中 callback 函数,到此为止完成了一次 setState 全过程

一个主要任务的先后顺序:render 阶段 render 函数执行 -> commit 阶段真实 DOM 替换 -> setState 回调函数执行 callback

1.2 类组件 setState 原理揭秘:

  • 类组件初始化过程中绑定了负责更新的 Updater 对象,调用 setState,实际上是 React 底层调用 Updater 对象上的 enqueueSetState 方法

    // react-reconciler/src/ReactFiberClassComponent.js
    
    enqueueSetState(){
         /* 每一次调用`setState`,react 都会创建一个 update 里面保存了 */
         const update = createUpdate(expirationTime, suspenseConfig);
         /* callback 可以理解为 setState 回调函数,第二个参数 */
         callback && (update.callback = callback) 
         /* enqueueUpdate 把当前的update 传入当前fiber,待更新队列中 */
         enqueueUpdate(fiber, update); 
         /* 开始调度更新 */
         scheduleUpdateOnFiber(fiber, expirationTime);
    }

    enqueueSetState 会创建一个 update,然后放入当前 fiber 对象的待更新队列中,最后开启调度更新,进入到(批量)更新流程

  • React 批量更新:

    // react-dom/src/events/DOMLegacyEventPluginSystem.js
    
    /* 在`legacy`模式下,所有的事件都将经过此函数同一处理 */
    function dispatchEventForLegacyPluginEventSystem(){
        // handleTopLevel 事件处理函数
        batchedEventUpdates(handleTopLevel, bookKeeping);
    }
    // react-dom/src/events/ReactDOMUpdateBatching.js
    
    function batchedEventUpdates(fn,a){
      /* 开启批量更新  */
      isBatchingEventUpdates = true;
      try {
        /* 这里执行了的事件处理函数, 比如在一次点击事件中触发setState,那么它将在这个函数内执行 */
        return batchedEventUpdatesImpl(fn, a, b);
      } finally {
        /* try 里面 return 不会影响 finally 执行  */
        /* 完成一次事件,批量更新  */
        isBatchingEventUpdates = false;
      }
    }
    • 在 React 事件执行之前通过 isBatchingEventUpdates = true打开开关,开启事件批量更新,当该事件结束,再通过isBatchingEventUpdates = false关闭开关,然后在·scheduleUpdateOnFiber `中根据这个开关来确定是否进行批量更新
    export default class index extends React.Component{
        state = { number:0 }
        handleClick= () => {
              this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback1', this.state.number)  })
              console.log(this.state.number)
              this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback2', this.state.number)  })
              console.log(this.state.number)
              this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback3', this.state.number)  })
              console.log(this.state.number)
        }
        render(){
            return <div>
                { this.state.number }
                <button onClick={ this.handleClick }  >number++</button>
            </div>
        }
    }

    0, 0, 0, callback1 1 ,callback2 1 ,callback3 1

  • 异步操作(promise 或者 setTimeout)打破批量更新规则:

    • setTimeout 中的 setState 为什么会多次 render?

      • 因为异步出了事件系统的执行上下文:触发时 isBatchingUpdates 为 false,所以多次 setState 没有被合并处理,能够直接进行更新
    setTimeout(()=>{
        this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback1', this.state.number)  })
        console.log(this.state.number)
        this.setState({ number:this.state.number + 1 },()=>{    console.log( 'callback2', this.state.number)  })
        console.log(this.state.number)
        this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback3', this.state.number)  })
        console.log(this.state.number)
    })

    callback1 1 , 1, callback2 2 , 2,callback3 3 , 3

  • 异步环境继续开启批量更新模式:
    React-Dom 中提供了批量更新方法 unstable_batchedUpdates,可以去手动批量更新

    import ReactDOM from 'react-dom'
    const { unstable_batchedUpdates } = ReactDOM
    
    setTimeout(()=>{
        unstable_batchedUpdates(()=>{
            this.setState({ number:this.state.number + 1 })
            console.log(this.state.number)
            this.setState({ number:this.state.number + 1})
            console.log(this.state.number)
            this.setState({ number:this.state.number + 1 })
            console.log(this.state.number) 
        })
    })

    0 , 0 , 0 , callback1 1 , callback2 1 ,callback3 1

    在实际工作中,unstable_batchedUpdates可以用于 Ajax 数据交互之后,合并多次 setState,或者时多次 useState —— 如果批量更新处理,一次数据交互多次改变 state 会促使视图多次渲染

  • 提升更新优先级:
    React-dom 提供了 flushSync ,flushSync 可以将回调函数中的更新任务,放在一个较高的优先级中

    handerClick=()=>{
        setTimeout(()=>{
            this.setState({ number: 1  })
        })
        
        this.setState({ number: 2  })
        
        ReactDOM.flushSync(()=>{
            this.setState({ number: 3  })
        })
        
        this.setState({ number: 4  })
    }
    
    render(){
       console.log(this.state.number)
       return ...
    }

    3 4 1

    首先 flushSync this.setState({ number: 3 })设定了一个高优先级的更新,然后 2 和 3 被批量更新到 3,最后更新 setTimeout 中的 1

    flushSync 中的 setState > 正常执行上下文中的 setState > setTimeout、Promise 中的 setState

1.3 函数组件 useState 原理揭秘

(未完待下一道面试题)18. 原理篇-hooks原理

2. V18concurrent模式下的 setState

(未完待下一道面试题)34. v18特性篇-concurrent 下的 state更新流程
原文:"首先 flushSync this.setState({ number: 3 })设定了一个高优先级的更新,然后 2 和 3 被批量更新到 3,最后更新 setTimeout 中的 1"
2,3批量更新到3是不是打错了? 是2,4批量更新到4

from daily-interview-question.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.