完整实现React day10
update
流程与mount
流程的区别。
对于beginWork
:
- 需要处理
ChildDeletion
的情况 - 需要处理节点移动的情况(abc -> bca)
对于completeWork
:
- 需要处理
HostText
内容更新的情况 - 需要处理
HostComponent
属性变化的情况
对于commitWork
:
- 对于
ChildDeletion
,需要遍历被删除的子树 - 对于
Update
,需要更新文本内容
对于useState
:
- 实现相对于
mountState
的updateState
beginWork流程
本节课仅处理单一节点,所以省去了「节点移动」的情况。我们需要处理:
- singleElement
- singleTextNode
处理流程为:
- 比较是否可以复用
current fiber
- 比较
key
,如果key
不同,不能复用 - 比较
type
,如果type
不同,不能复用 - 如果
key
与type
都相同,则可复用 - 不能复用,则创建新的(同
mount
流程),可以复用则复用旧的
注意:对于同一个fiberNode
,即使反复更新,current
、wip
这两个fiberNode
会重复使用
completeWork流程
主要处理「标记Update」的情况,本节课我们处理HostText
内容更新的情况。
commitWork流程
对于标记ChildDeletion
的子树,由于子树中:
- 对于
FC
,需要处理useEffect
unmout
执行、解绑ref
- 对于
HostComponent
,需要解绑ref
- 对于子树的
根HostComponent
,需要移除DOM
所以需要实现「遍历ChildDeletion子树」的流程
对于useState
需要实现:
- 针对
update
时的dispatcher
- 实现对标
mountWorkInProgresHook
的updateWorkInProgresHook
- 实现
updateState
中「计算新state的逻辑」
其中updateWorkInProgresHook
的实现需要考虑的问题:
- hook数据从哪来?
- 交互阶段触发的更新
import { useState } from 'react'; import { Dispatch } from 'react/src/currentDispatcher'; import { Dispatcher } from 'react/src/currentDispatcher'; import internals from 'shared/internals'; import { Action } from 'shared/ReactTypes'; import { FiberNode } from './fiber'; import { createUpdate, createUpdateQueue, enqueueUpdate, processUpdateQueue, UpdateQueue } from './updateQueue'; import { scheduleUpdateOnFiber } from './workLoop'; let currentlyRenderingFiber: FiberNode | null = null; let workInProgressHook: Hook | null = null; let currentHook: Hook | null = null; const { currentDispatcher } = internals; interface Hook { memoizedState: any; updateQueue: unknown; next: Hook | null; } export function renderWithHooks(wip: FiberNode) { // 赋值操作 currentlyRenderingFiber = wip; // 重置 hooks链表 wip.memoizedState = null; const current = wip.alternate; if (current !== null) { // update currentDispatcher.current = HooksDispatcherOnUpdate; } else { // mount currentDispatcher.current = HooksDispatcherOnMount; } const Component = wip.type; const props = wip.pendingProps; // FC render const children = Component(props); // 重置操作 currentlyRenderingFiber = null; workInProgressHook = null; currentHook = null; return children; } const HooksDispatcherOnMount: Dispatcher = { useState: mountState }; const HooksDispatcherOnUpdate: Dispatcher = { useState: updateState }; function updateState<State>(): [State, Dispatch<State>] { // 找到当前useState对应的hook数据 const hook = updateWorkInProgresHook(); // 计算新state的逻辑 const queue = hook.updateQueue as UpdateQueue<State>; const pending = queue.shared.pending; if (pending !== null) { const { memoizedState } = processUpdateQueue(hook.memoizedState, pending); hook.memoizedState = memoizedState; } return [hook.memoizedState, queue.dispatch as Dispatch<State>]; } function updateWorkInProgresHook(): Hook { // TODO render阶段触发的更新 let nextCurrentHook: Hook | null; if (currentHook === null) { // 这是这个FC update时的第一个hook const current = currentlyRenderingFiber?.alternate; if (current !== null) { nextCurrentHook = current?.memoizedState; } else { // mount nextCurrentHook = null; } } else { // 这个FC update时 后续的hook nextCurrentHook = currentHook.next; } if (nextCurrentHook === null) { throw new Error( `组件${currentlyRenderingFiber?.type}本次执行时的Hook比上次执行时多` ); } currentHook = nextCurrentHook as Hook; const newHook: Hook = { memoizedState: currentHook.memoizedState, updateQueue: currentHook.updateQueue, next: null }; if (workInProgressHook === null) { // mount时 第一个hook if (currentlyRenderingFiber === null) { throw new Error('请在函数组件内调用hook'); } else { workInProgressHook = newHook; currentlyRenderingFiber.memoizedState = workInProgressHook; } } else { // mount时 后续的hook workInProgressHook.next = newHook; workInProgressHook = newHook; } return workInProgressHook; } function mountState<State>( initialState: (() => State) | State ): [State, Dispatch<State>] { // 找到当前useState对应的hook数据 const hook = mountWorkInProgresHook(); let memoizedState; if (initialState instanceof Function) { memoizedState = initialState(); } else { memoizedState = initialState; } const queue = createUpdateQueue<State>(); hook.updateQueue = queue; hook.memoizedState = memoizedState; // @ts-ignore const dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue); queue.dispatch = dispatch; return [memoizedState, dispatch]; } function dispatchSetState<State>( fiber: FiberNode, updateQueue: UpdateQueue<State>, action: Action<State> ) { const update = createUpdate(action); enqueueUpdate(updateQueue, update); scheduleUpdateOnFiber(fiber); } function mountWorkInProgresHook(): Hook { const hook: Hook = { memoizedState: null, updateQueue: null, next: null }; if (workInProgressHook === null) { // mount时 第一个hook if (currentlyRenderingFiber === null) { throw new Error('请在函数组件内调用hook'); } else { workInProgressHook = hook; currentlyRenderingFiber.memoizedState = workInProgressHook; } } else { // mount时 后续的hook workInProgressHook.next = hook; workInProgressHook = hook; } return workInProgressHook; }