完整实现React day10

update流程与mount流程的区别。

对于beginWork

  • 需要处理ChildDeletion的情况
  • 需要处理节点移动的情况(abc -> bca)

对于completeWork

  • 需要处理HostText内容更新的情况
  • 需要处理HostComponent属性变化的情况

对于commitWork

  • 对于ChildDeletion,需要遍历被删除的子树
  • 对于Update,需要更新文本内容

对于useState

  • 实现相对于mountStateupdateState

beginWork流程

本节课仅处理单一节点,所以省去了「节点移动」的情况。我们需要处理:

  • singleElement
  • singleTextNode

处理流程为:

  1. 比较是否可以复用current fiber
    1. 比较key,如果key不同,不能复用
    2. 比较type,如果type不同,不能复用
    3. 如果keytype都相同,则可复用
  2. 不能复用,则创建新的(同mount流程),可以复用则复用旧的

注意:对于同一个fiberNode,即使反复更新,currentwip这两个fiberNode会重复使用

completeWork流程

主要处理「标记Update」的情况,本节课我们处理HostText内容更新的情况。

commitWork流程

对于标记ChildDeletion的子树,由于子树中:

  • 对于FC,需要处理useEffect unmout执行、解绑ref
  • 对于HostComponent,需要解绑ref
  • 对于子树的根HostComponent,需要移除DOM

所以需要实现「遍历ChildDeletion子树」的流程

对于useState

需要实现:

  • 针对update时的dispatcher
  • 实现对标mountWorkInProgresHookupdateWorkInProgresHook
  • 实现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;
    }

     

posted @ 2023-04-28 17:11  刷刷题啊呀呀  阅读(22)  评论(0编辑  收藏  举报