浅析React Hook之useReducer

浅析React Hook之useReducer

提示:本文主要讲解useReducer



认识Fiber

Fiber是什么?

Fiber是一种数据结构,用于描述虚拟DOM。
react目前的做法是使用链表。每个virtualDOM节点内部表示为一个Fiber

Fiber树

顾名思义,将Fiber连接起来就是Fiber树
在这里插入图片描述

Fiber是一个执行单单元

Fiber是一个执行单元,每次执行完一个执行单元, React 就会检查现在还剩多少时间,如果没有时间就将控制权让出去

认识循环链表

循环链表是另一种形式的链式存储结构

它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环
在这里插入图片描述

思考:这种结构该如何实现???

代码如下(示例):

function enqueueUpdate(queue, action){
	const update = {action, next:null}
	//TODO ?
}

认识Hook

什么是hook?

Hook是React 16.8的新增特性。它可以让你在不便携class的情况下使用state以及其他的React特性。

为什么会引入hook

官方给出的动机是解决长时间使用和维护react过程中经常遇到的问题。
1、在组件之间复用状态逻辑很难(比如:render、props和高阶组件)
2、复杂组件变得难以理解(比如:不想干的逻辑放在一起)
3、难以理解的class(比如:this的工作方式)

常见hook

在这里插入图片描述

如何实现useReducer

用法:略

注册Hook流程

在这里插入图片描述

ReactFiberHooks

// 调度更新的方法
// import { scheduleUpdateOnFiber } from 'ReactFiberWorkLoop';

// ReactCurrentDispatcher是暴露给用户调用hook的接口,兼容mount和update时,调用相同hook,实现不同逻辑。(面向接口编程)
/**
 * 
 interface IReactCurrentDispatcherValue {
     useReducer: <T>(reducer: (state: T, action: any) => T, initialState?: T) => [state: T, dispatch: (action: any) => void]
     useState: ...
 }
*/
let ReactCurrentDispatcher = {
    current: null
}

// 当前渲染的Fiber
let currentlyRenderingFiber = null;
// 当前工作的hook
let workInProgressHook = null;
// 老hook
let currentHook = null;

// 初次渲染时🐶
const HooksDispatcherOnMount = {
    useReducer: mountReducer
}

// 更新时的🐶
const HooksDispatcherOnUpdate = {
    useReducer: updateReducer
}

// 暴露用户使用的useReducer
export function useReducer(reducer, initialArg) {
    return ReactCurrentDispatcher.current.useReducer(reducer, initialArg);
}

// 顾名思义,调用函数组件返回children的方法,只不过带上了hook
// current 老fiber
// workInProgress 新fiber(当前执行的fiber)
// Component
export function renderWithHooks(_current, workInProgress, Component) {
    // 当前render的fiber
    currentlyRenderingFiber = workInProgress;
    // 清空fiber的memoizedState状态,这个是在fiber上用来保存函数组件hook的属性,它是链表结构
    workInProgress.memoizedState = null;
    // 根据是否存在老fiber判断是否是初次渲染
    if (_current !== null) {
            // 更新逻辑
            ReactCurrentDispatcher.current = HooksDispatcherOnUpdate;
    } else {
            // 加载逻辑
            ReactCurrentDispatcher.current = HooksDispatcherOnMount;
    }
    // 这里就是注册hook,同时返回函数组件的children
    let children = Component();
    // 清空数据
    currentlyRenderingFiber = null;
    currentHook = null;
    workInProgressHook = null;
    return children;
}

// 更新Reducer
// 更新是不需要初始值的
function updateReducer(reducer) {
    // 构建更新hook,并将起添加hooks链表中
    const hook = updateWorkInProgressHook();
    const queue = hook.queue;
    // 将reducer重新赋值给queue中
    // 想一想reducer此时会存在什么问题???
    queue.lastRenderedReducer = reducer;
    // 将老hook
    const current = currentHook;
    // pending就是我们dispatch时将更新状态储存在链表结构中
    const pendingQueue = queue.pending;
    // 开始实现更新状态
    if (pendingQueue !== null) {
        // 先拿到更新链表中的第一数据
        const first = pendingQueue.next;
        // 之前的state
        let newState = current.memoizedState;
        let update = first;
        // 如果不存在更新的数据或者循环到头了结束
        do {
            const action = update.action;
            newState = reducer(newState, action);
            update = update.next;
        } while (update !== null &amp;&amp; update !== first);
        // 清空更新队列
        queue.pending = null;
        // 更新state
        hook.memoizedState = newState;
        // 更新队列上的上一次state
        queue.lastRenderedState = newState;
    }
    const dispatch = dispatchAction.bind(null,currentlyRenderingFiber,queue);
    return [hook.memoizedState, dispatch];
}

// 当前工作的hook
function updateWorkInProgressHook() {
    // 更新逻辑需要从老fiber的memoizedState去获取hook链表
    // 如果nextCurrentHook不存在,说明第一hook,从老fiber上拿到
    // 如果存在就可以从nextCurrentHook的next中拿到
    let nextCurrentHook;
    if (currentHook === null) {
        const current = currentlyRenderingFiber.alternate;
        nextCurrentHook = current.memoizedState;
    } else {
        nextCurrentHook = currentHook.next;
    }
    // 老fiber的hook
    currentHook = nextCurrentHook;
    // 重用hook逻辑
    const newHook = {
        // memoizedState保存的时上次的state
        memoizedState: currentHook.memoizedState,
        // 老fiber的更新队列
        queue: currentHook.queue,
        next: null,
    };
    // 通过重用老fiber的hook逻辑,将其用于构建新hook
    // 如果不存在工作的hook,那么currentlyRenderingFiber.memoizedState 和 workInProgressHook都需要更新
    // 否则通过next将其连接起来
    if (workInProgressHook === null) {
        currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
    } else {
        workInProgressHook = workInProgressHook.next = newHook;
    }
    // 返回的当前工作的hook
    return workInProgressHook;

}
export function mountReducer(reducer, initialArg) {
    // 构建hook以及添加hook链表中
    const hook = mountWorkInProgressHook();
    // 初始化状态
    let initialState = initialArg;
    hook.memoizedState = initialState;
    // 构建reducer的更新队列
    // lastRenderedReducer上一次的reducer
    // lastRenderedState 上一次状态
    const queue = (hook.queue = {pending: null, lastRenderedReducer: reducer, lastRenderedState: initialState});
    // dispatch 绑定了当前fiber和更新队列
    const dispatch = dispatchAction.bind(null,currentlyRenderingFiber,queue);
    // 返回state和更新方法
    return [hook.memoizedState, dispatch];
}

// 加载时创建hook及构建hook关系的方法
export function mountWorkInProgressHook() {
    // hook
    // memoizedState记忆state值
    // queue 更新state的队列,我们可以调用很多次dispatch,那么这个值就会存在这个队列中的queue.pending上。
    // next 下一个hook

    // e.g
    /**
     {
         action: {type: 'ADD', payload: {number: 1}},
         next: null
     } 
    */ 
    const hook = {
        memoizedState: null,
        queue: null,
        next: null,
    };

    // 构建hook关系
    if (workInProgressHook === null) {
        currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
    } else {
        workInProgressHook = workInProgressHook.next = hook;
    }
    return workInProgressHook;
}

export function dispatchAction(fiber, queue, action) {
  // action 需要更新的状态
  const update = { action, next: null };
  const pending = queue.pending;
  // 在queue上构建链表
  if (pending === null) {
      update.next = update;
  } else {
      update.next = pending.next;
      pending.next = update;
  }
  queue.pending = update;
  // 对比新状态是否和老状态相同,决定是否更新
  const lastRenderedReducer = queue.lastRenderedReducer;
  const currentState = queue.lastRenderedState;
  const eagerState = lastRenderedReducer(currentState, action);
  if (Object.is(eagerState, currentState)) {
      return
  }
  // 将其fiber放到更新队列上
  scheduleUpdateOnFiber(fiber);
}

useState

思考一下useState和useReducer是不是有相同的点?那怎么实现呐。。。

期待更多。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值