本次课程我们来开始实现一下mini-react中的useEffect。我们先来是先最简单的,没有依赖的情况。
在app.js加上useEffect
jsx
React.useEffect(() => {
console.log('useEffect')
}, [])
创建函数,先存起来参数
jsx
function useEffect(callback, deps) {
const effctHook = {
callback,
deps
}
}
调用时机
- 应该在dom的渲染之后,也就是处理完所有的节点之后调用
- react.js
jsx
function commitRoot() {
deletions.forEach(commitDeletion)
commitWork(wipRoot.child)
commitEffectHook()
currentRoot = wipRoot
wipRoot = null
deletions = []
}
function commitEffectHook() {
function run(fiber) {
if (!fiber) {
return
}
fiber?.effectHook?.callback()
run(fiber.child)
run(fiber.sibling)
}
run(wipFiber)
}
function useEffect(callback, deps) {
const effectHook = {
callback,
deps
}
wipFiber.effectHook = effectHook
}
const React = {
render,
useState,
useEffect,
createElement,
update
}
1.png
增加依赖项count
- app.jsx
jsx
import React from "./core/React.js";
function Foo() {
console.log('fooo')
const [bar, setBar] = React.useState('bar')
function handleClick() {
setBar('bar')
setCount(c => c + 1)
}
const [count, setCount] = React.useState(10);
React.useEffect(() => {
console.log('useEffect')
}, [count])
return (
<div>
{bar}
{count}
<button onClick={handleClick}>click</button>
</div>
);
}
function App() {
return (
<div>
<Foo />
</div>
);
}
export default App;
- 更新核心代码react.js
jsx
function commitEffectHook() {
function run(fiber) {
// ...
if(!fiber.alternate) {
fiber.effectHook?.callback()
}else {
// update
// deps 有没有发生改变
const oldEffectHook = fiber.alternate?.effectHook
// some
const needUpdate = oldEffectHook?.deps.some((oldDep, index) => {
return oldDep !== fiber.effectHook.deps[index]
})
needUpdate && fiber.effectHook?.callback()
}
// ...
}
run(wipFiber)
}
开始实现多个
- app.jsx
jsx
React.useEffect(() => {
console.log('init')
}, [])
React.useEffect(() => {
console.log('update', count)
}, [count])
- react.js
jsx
function commitRoot() {
deletions.forEach(commitDeletion)
commitWork(wipRoot.child)
commitEffectHooks()
currentRoot = wipRoot
wipRoot = null
deletions = []
}
function commitEffectHooks() {
function run(fiber) {
if (!fiber) {
return
}
if(!fiber.alternate) {
fiber?.effectHooks?.forEach((hook) => {
hook.callback()
})
}else {
// update
// deps 有没有发生改变
fiber.effectHooks?.forEach((newHook, index)=> {
if(newHook.deps.length> 0 ) {
const oldEffectHook = fiber.alternate?.effectHooks[index]
// some
const needUpdate = oldEffectHook?.deps.some((oldDep, i) => {
return oldDep !== newHook.deps[i]
})
needUpdate && newHook.callback()
}
})
}
run(fiber.child)
run(fiber.sibling)
}
run(wipFiber)
}
function updateFunctionComponent(fiber) {
effectHooks = []
stateHooks = []
stateHookIndex = 0
wipFiber = fiber
const children = [fiber.type(fiber.props)]
reconcileChildren(fiber, children)
}
let effectHooks
function useEffect(callback, deps) {
const effectHook = {
callback,
deps
}
effectHooks.push(effectHook)
wipFiber.effectHooks = effectHooks
}