useEffect 返回函数执行过程源码解析

这篇文章主要为大家介绍了useEffect 返回函数执行过程源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

本文对应的 react 版本是 18.2.0

掌握 React 组件树遍历技巧中说到 react 是怎么遍历 dom

那么在遍历的过程中,如果发现当前节点有子节点被删除了,那么 react 会怎么处理呢?

下面是源码简化:这里是完整的源码

function recursivelyTraversePassiveUnmountEffects(parentFiber: Fiber): void { const deletions = parentFiber.deletions; if ((parentFiber.flags & ChildDeletion) !== NoFlags) { if (deletions !== null) { for (let i = 0; i 

deletions

在正式开始之前,我们要了解一个 fiber 的属性:deletions

这个属性存放的是当前节点中被删除的 fiber,这个数组是在 commit 阶段被赋值的

如果有被删除的节点,这个属性值是一个数组,如果没有被删除的节点,这个属性值是 null

const A = () => { useEffect(() => { return () => { console.log("A unmount"); }; }, []); return 
文本A
; }; const B = () => { useEffect(() => { return () => { console.log("B unmount"); }; }, []); return
文本B
; };

如果 App 组件这样写,那么 deletions 的值是 [FiberNode, FiberNode]

const App(){ const [count, setCount] = useState(0) return  } 

如果 App 组件这样写,那么 deletions 的值是 [FiberNode]

对于第二种情况,react 会把 A 组件和 B 组件作为一个整体,所以 deletions 的值是 [FiberNode]

处理当前节点的 deletions

react 在遍历 fiber tree 时,会先处理当前的 fiberdeletions,等处理完之后再遍历下一个 fiber

现在我们已经知道 deletions 中保存的是当前 fiber 下被删除的子节点

这时 react 会遍历 deletions 数组,然后执行每个 fiberpassive effect 返回的函数

但是有个问题,如果 deletions 中的 fiber 有子节点,那么这些子节点也会被删除,这时 react 会怎么处理呢?

这里分两种情况来讨论:

删除的 fiber 没有子节点:

{xxxx && }

这种情况比较好理解

当遍历到 div 时,因为 节点会被卸载,所以在 divdeletions 保存了一个 fiber

遍历 deletions 数组,执行 passive effect 返回的函数

如下图所示:

删除的 fiber 有子节点:

{xxxx && <>}

这种情况就比较复杂了

当遍历到 div 时,<> 节点会被卸载,所以在 divdeletions 保存了一个 <>fiber

遍历 deletions 数组,执行 fiberpassive effect 返回的函数,对于 <> 来说是不存在的 passive effect

那么这个时候就要去遍历它的 child.fiber,也就是

首先拿到第一个 fiber,也就是 ,然后执行 passive effect 返回的函数,这步比较好理解

这里遍历也是深度优先,遍历一个 child,执行一个 passive effect 返回函数,然后再遍历下一个 child(这边 已经是叶子节点了)

然后拿到第二个 fiber,也就是 ,然后执行 passive effect 返回的函数,这步就不太好理解了

这里要注意的是:

react 在寻找有 passive effectfiber 时,只遍历到有 passive effectfiber, 像 div 这种没有 passive effect 就不会遍历

但是在处理 deletionsreact 会遍历所有的 fiber,也就是说从当前的 fiber 开始,一直往下遍历到叶子节点,这个叶子节点是指文本节点这种,往下不会有节点了(对于 A 组件来说 文本A 是文本节点)

然后在开始往上遍历,往上遍历是调用 commitPassiveUnmountEffectsInsideOfDeletedTree_complete 函数,直到遍历到 deletionRoot,在向上遍历的过程中会检查是否有 sibling,如果有说明 sibling 还没被处理,这样就找到了 ,然后执行 passive effect 返回的函数

如下图所示:

向下遍历和向上遍历

在处理 deletions 时,对于每个 deletedNode,都先向下遍历,然后再向上遍历

  • 向下遍历:commitPassiveUnmountEffectsInsideOfDeletedTree_begin(深度优先,优先处理左边的节点)
  • 向上遍历:commitPassiveUnmountEffectsInsideOfDeletedTree_complete(之后再处理右边节点)

总结

1. 遍历 deletions 数组:

  • react 在处理 deletions 时,先沿着 fiber tree 向下遍历,如果有 passive effect 返回的函数,则执行
  • 一直遍历到没有 childfiber,再向上遍历,处理 sibling
  • 再向上遍历时,如果如果遇到 sibling,再向下遍历,向下遍历时遇到 passive effect 返回的函数,则执行
  • 如此循环直到遍历到 deletedNode,结束遍历

2. 结合掌握 React 组件树遍历技巧

  • 遍历寻找有 passive effect 节点
    • react 从根组件向下遍历,如果没有 passive effect,则不会遍历
  • 遍历时,如果遇到当前节点有 deletions 时,会暂停寻找 passive effect 节点
    • 进入遍历 deletions 数组

react 遍历 deletions 完整逻辑如下图所示:

图中绿色部分是遍历 deletionsNode 过程,红色部分是遍历寻找 passive effect 过程

以上就是useEffect 返回函数执行过程源码解析的详细内容,更多关于useEffect 返回函数执行的资料请关注0133技术站其它相关文章!

以上就是useEffect 返回函数执行过程源码解析的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » JavaScript 教程