-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React Hooks: useState #3
Comments
|
M |
useRe
useReducer 不在这里分析了,这篇文章涉及的东西太多了,决定把 useReducer 作为独立章节。 |
自动批处理作为 v18 的新特性,只有『自动』两个字能叫新 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
React Hooks: useState 分析
说明
TLNR
mount 场景下的 useState
直接对 useState 打断点进行调试,我们会进入 mountState 内
就这样 mount 阶段的 useState 的分析完了,东西很少。
另外请注意一下 basicStateReducer 这一函数。
总结
mount 阶段的 useState 大致起到一个初始化的作用,在结束该函数的调用后我们会获得该 useState 对应的 hooks 对象。该对象长这样
触发 useState 的 dispatch
此时将断点打到 useState 返回的 dispatch 上,触发点击事件。获得下面的调用栈
dispatchSetState 是个关键的函数,再次强调一下。
总结一下 useState 的 dispatch
对于 useState 的 dispatch 来说,每调用一次 dispathc 都会生成一个新的 update 实例且该实例会被挂载到其 hooks 的链表,但是调用 dispatch 后生成的新值会与其原始值(currentState)经过一次浅比较来决定这一次的更新是否需要被调度更新。在 v18 的版本中,这一次的调度任务是存在于 microTask 中的,因此可认为 useState 的 dispatch 属于异步操作。下面简单写个例子验证下。
点击后得到的结果
其调用栈,不难看到 microtask。
update 场景下的 useState
接下来我们稍微在原来的调试代码上添加一个 dispatch,新的调试代码大概长这样。
对整个页面的表现做一个记录,大致长这样
顺带一提
第二次 dispatch 时因为会不满足 fiber.lanes === NoLanes 这个条件因此直接跳入 enqueueConcurrentHookUpdate (具体看一下 dispatchSetState 的代码)
而在 enqueueConcurrentHookUpdate 对 update 链表连接后我们会得到如下结构的环状链表
回到正题,在 useState 的 update 阶段中会调用 updateState 内的 updateReducer 这一函数(就是 uesReducer 对应的 mount 版本)
总结一下
useState 的 update 阶段会通过 dispatch 函数生成的 update 链表去更新对应的状态,然而并非所有的 update 都会被更新,只有其优先级(lane)属于当前 renderLanes 内才会被优先计算,而跳过的 update 会被标记并在下一次 render 时被更新。
简单提供个例子来证明
从运行结果来看,由于 timeout1 的 update 被 startTransition降级,因此 timeout2 的 update 被优先更新了。
批处理
批处理其实在 v17 时代便已经有了,不过 v18 把它的限制条件给去除了然后就成了自动批处理(恼)。具体可以看看这篇文章。
比方说我们这么一段代码在运行(当然这里的调试环境依然是 v18)
根据上文中对 dispatchSetState 的分析中其实有提到,只要是有必要更新到页面上的 update 就会调用 scheduleUpdateOnFiber 来触发渲染。那么例子中连续调用了两次 dispatch ,也就是说触发了两次 scheduleUpdateOnFiber,那么问题来了——页面渲染了几次呢?
就一次。
至于为什么需要去看 scheduleUpdateOnFiber 里面做了什么,scheduleUpdateOnFiber 涉及到了整个更新的入口,因此也是一个比较重要的函数。
该函数主要负责处理
scheduleUpdateOnFiber
这时我们又遇到了一个 react 内的重要函数 —— ensureRootIsScheduled,进入该函数就能看到任务调度在 react 内的核心逻辑了(还有一部分核心逻辑在 Scheduler 内,可以看我写的 React Scheduler: Scheduler 源码分析)
ensureRootIsScheduled
分析完 ensureRootIsScheduled 我们也对批处理是如何实现的有了大致的了解,它的实现其实就是对 react 内同优先级任务的复用,这样子就做到了多次触发 hooks 的 dispatch 但是只渲染了一次。
不过到这配合上 React Scheduler: Scheduler 源码分析 其实我们已经把 react 内三大模块(调度 → 协调 → 渲染),调度的部分全部介绍完了,接下来让我一个问题来把整篇文章串起来。
React从触发 useState 的 dispatch 到渲染到页面做了哪些事 —— 调度篇
The text was updated successfully, but these errors were encountered: