Skip to content

实现useEffect工作流程

本节课代码地址(请参考课程获取) 实现useEffect 需要考虑的: effect 数据结构 effect 的工作流程如何接入现有流程

effect数据结构

什么是effect

js
function App() {
useEffect(() => {

// create

return() => {

// destroy

}

}, [xxx, yyy])

useLayoutEffect(() => {})

useEffect(() => {}, [])
// ...

}

数据结构需要考虑: 不同effect 可以共用同一个机制

useEffect
useLayoutEffect
useInsertionEffect

需要能保存依赖 需要能保存create 回调 需要能保存destroy 回调 需要能够区分是否需要触发create 回调

mount 时

依赖变化时

const effect = {

tag,

create,

destroy,

deps,

next

}

注意区分本节课新增的3个flag : 对于fiber ,新增PassiveEffect ,代表「当前fiber本次更新存在副作用」 对于effect hookPassive 代表「useEffect对应effect」 对于effect hookHookHasEffect 代表「当前effect本次更新存在副作用」 为了方便使用,最好和其他effect 连接成链表

render 时重置effect 链表

effect的工作流程

调度副作用 调度需要使用Scheduler(调度器),调度器也属于React项目下的模块。在本课程中,我们不 会实现调度器,但会教如何使用它。

bash
pnpm i -w scheduler

pnpm i -D -w @types/scheduler

收集回调 回调包括两类:

create 回调
destroy 回调

在线Demo地址

jsx
function App() {

const [numupdateNum= useState(0);

useEffect(() => {

console.log('App mount');

}, []);

useEffect(() => {

console.log('num change create', num);

return() => {

console.log('num change destroy', num);

};

}, [num]);

return (

<div onClick={() => updateNum(num + 1)}>

{num === 0 ? <Child /> : 'noop'}

</div>

);

}

function Child() {

useEffect(() => {
console.log('Child mount');

return() =>console.log('Child unmount');

}, []);
return 'i am child';

}

这意味着我们需要收集两类回调: unmout 时执行的destroy 回调 update 时执行的create 回调 执行副作用 本次更新的任何create 回调都必须在所有上一次更新的destroy 回调执行完后再执行。 整体执行流程包括:

  1. 遍历effect
  2. 首先触发所有unmount effect ,且对于某个fiber ,如果触发了unmountdestroy ,本次更新不会再触发update create
  3. 触发所有上次更新的destroy
  4. 触发所有这次更新的createmountupdate 时的区别 mount 时:一定标记PassiveEffectupdate 时:deps 变化时标记PassiveEffect

纠错 useEffect 回调函数的执行依赖于:

1. 调度flushPassiveEffects 执行
  1. 收集需要执行的回调函数(commitMutationEffects 方法) 其中1需要判断PassiveMask ,当前2只判断了MutationMask ,这导致useEffect deps 变化后无法触发回调,所以2也需要增加PassiveMask 的判断:
// 增加PassiveMask

const subtreeHasEffect =

(finishedWork.subtreeFlags & (MutationMask | PassiveMask)) !== NoFlags;

const rootHasEffect =

(finishedWork.flags & (MutationMask | PassiveMask)) !== NoFlags;

详见fix: useEffect回调不收集的情况

用心学习,用代码说话 💻