七、useEffect
2024-03-30 本文已影响0人
sweetBoy_9126
- 实现初始化
初始化加载 onMounted
用法:
useEffect(() => {
console.log(1)
}, [])
实现:
const useEffect = (callback, deps) => {
const effectHook = {
callback,
deps
}
// 这里之所以用 wipFiber 因为它只有在每次有函数组件的时候才会调用,所以它肯定是一个函数组件
wipFiber.effectHook = effectHook
}
因为我们要在 onMounted 里调用所以就要在节点都挂载完成的时候也就是我们的 commitWork 后
const commitRoot = () => {
deletions.forEach(commitDeletion)
commitWork(wipRoot.child)
+ commitEffectHooks()
...
}
const commitEffectHook = () => {
const run = (fiber) => {
if (!fiber) return
fiber.effectHook?.callback()
run(fiber.child)
run(fiber.sibling)
}
// 这里不用 wipFiber 的原因是 wipFiber 每次会被覆盖,如果有多个函数组件那么只会执行最后一个的,所以我们需要从根节点开始逐层遍历
run(wipRoot)
}
- 实现 update
用法:
const [count, setCount] = React.useState(0)
const handleClick = () => {
setCount((count) => count + 1)
}
React.useEffect(() => {
console.log(11111)
}, [count])
实现我们需要区分初始化和 update,这个我们可以通过我们的 alternate 指针来区分,没有就是初始化,然后需要对比老的新的依赖项的每一项和老的依赖项的对应的项值有没有变化,只要有一个值不一样我们就要重新调用
const commitEffectHook = () => {
const run = (fiber) => {
if (!fiber) return
if (!fiber.alternate) {
// init
fiber.effectHook?.callback()
} else {
// update
const oldEffect = fiber.alternate?.effectHook
const needUpdate = fiber.effectHook?.deps.some((dep, index) => {
return dep !== oldEffect.deps[index]
})
needUpdate && fiber.effectHook?.callback()
}
run(fiber.child)
run(fiber.sibling)
}
run(wipRoot)
}
- 实现多个 useEffect
let effectHooks
const useEffect = (callback, deps) => {
const effectHook = {
callback,
deps
}
effectHooks.push(effectHook)
wipFiber.effectHooks = effectHooks
}
const commitEffectHooks = () => {
const run = (fiber) => {
if (!fiber) return
if (!fiber.alternate) {
// init
fiber.effectHooks?.forEach(hook => hook?.callback())
} else {
// update
fiber.effectHooks?.forEach((newHook, index) => {
const oldEffect = fiber.alternate?.effectHooks[index]
const needUpdate = newHook?.deps.some((dep, i) => {
return dep !== oldEffect.deps[i]
})
needUpdate && newHook?.callback()
})
}
run(fiber.child)
run(fiber.sibling)
}
run(wipRoot)
}
- 实现 cleanup
1). 存下useEffect 返回的 cleanup 函数,也就是调用useEffect 的 callback 把返回值存下来
const useEffect = (callback, deps) => {
const effectHook = {
callback,
deps,
+ cleanup: undefined
}
effectHooks.push(effectHook)
wipFiber.effectHooks = effectHooks
}
const run = (fiber) => {
if (!fiber) return
if (!fiber.alternate) {
// init
+ fiber.effectHooks?.forEach(hook => (hook.cleanup = hook?.callback()))
} else {
// update
fiber.effectHooks?.forEach((newHook, index) => {
if (newHook.deps.length > 0) {
const oldEffect = fiber.alternate?.effectHooks[index]
const needUpdate = newHook?.deps.some((dep, i) => {
return dep !== oldEffect.deps[i]
})
+ needUpdate && (newHook.cleanup = newHook?.callback())
}
})
}
}
2). 在执行当前所有的 useEffect 之前调用
+ const runCleanup = (fiber) => {
+ if (!fiber) return
// 取上一次的 effectHooks
+ fiber.alternate?.effectHooks?.forEach(hook => {
// [] 不应该执行
+ if (hook.deps.length > 0) {
+ hook.cleanup?.()
+ }
+ })
+ runCleanup(fiber.child)
+ runCleanup(fiber.sibling)
+ }
+ runCleanup(wipRoot)
run(wipRoot)