Skip to content

useReducer挂载源码实现

index.js

js
import * as React from 'react';
import { createRoot } from "react-dom/client";
function getAge(state, action) {
    switch(action.type){
        case 'add':
            return state + action.value
        default:
            return state
    }
}
function MyFunctionComponent() {
  const [number, setAge] = React.useReducer(getAge, 0);
  return <button onClick={() => {
    setAge({ type: 'add', value: 1 });
    setAge({ type: 'add', value: 1 });
  }}>+age:{number}</button>
}
const root = createRoot(document.getElementById("root"));
root.render(<MyFunctionComponent />);

packages/react/src/ReactHooks.js

js
// 导入ReactCurrentDispatcher
import ReactCurrentDispatcher from './ReactCurrentDispatcher';

/**
 * 返回当前的React Dispatcher
 * @function
 * @return {Object} - 当前的React Dispatcher
 */
function resolveDispatcher() {
  return ReactCurrentDispatcher.current;
}

/**
 * 使用指定的reducer函数和初始参数调用当前dispatcher的useReducer方法
 * @function
 * @param {Function} reducer - 一个接收两个参数并返回新的state的函数,第一个参数为当前state,第二个参数为派发的action
 * @param {*} initialArg - 作为reducer函数的初始参数,返回的新的state
 * @return {Array} - 包含最新state和dispatch函数的数组
 */
export function useReducer(reducer, initialArg) {
  const dispatcher = resolveDispatcher();
  return dispatcher.useReducer(reducer, initialArg);
}

packages/react/src/ReactCurrentDispatcher.js

js
const ReactCurrentDispatcher = {
    current: null
}
export default ReactCurrentDispatcher;

packages/react/src/React.js

js
import { useReducer } from './ReactHooks';
import ReactSharedInternals from './ReactSharedInternals';
export {
  useReducer,
  ReactSharedInternals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
}

packages/shared/ReactSharedInternals.js

js
import * as React from 'react';

// 从React库中获取内部模块,这个内部模块包含了React的一些内部实现,通常不应该在应用代码中使用
// 由于这是一个内部模块,所以它的名字包含了 "__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED" 这样的警告
// 这是为了提醒开发者不要在应用代码中使用这些内部模块,否则可能导致应用的不稳定甚至崩溃
const ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED

// 将这个内部模块导出,这样其他模块就可以使用它了
// 再次提醒,通常情况下,我们不应该在应用代码中使用这些内部模块
export default ReactSharedInternals

packages/react-reconciler/src/ReactFiberHooks.js

js
import ReactSharedInternals from "shared/ReactSharedInternals";

const { ReactCurrentDispatcher } = ReactSharedInternals;

// 设置当前正在渲染的Fiber节点以及进行中的Hook为null
let currentlyRenderingFiber = null;
let workInProgressHook = null;

// Dispatcher对象在组件Mount时使用的Hooks
const HooksDispatcherOnMount = {
  useReducer: mountReducer  // 在mount期间,使用mountReducer处理useReducer
}

/**
 * Mount期间用于处理useReducer hook的函数
 * @param {Function} reducer - 进行state更新的函数
 * @param {*} initialArg - reducer函数的初始参数
 * @returns {Array} - 包含最新state和dispatch函数的数组
 */
function mountReducer(reducer, initialArg) {
  const hook = mountWorkInProgressHook();
  hook.memoizedState = initialArg;  // 记录当前hook的初始状态
  const queue = {
    pending: null  // 初始化pending队列为null
  }
  hook.queue = queue;  // 将queue对象赋值给hook的queue属性
  const dispatch = dispatchReducerAction.bind(null, currentlyRenderingFiber, queue);  // 创建dispatch函数,用于后续的action派发
  return [hook.memoizedState, dispatch];  // 返回包含最新状态和dispatch函数的数组
}

/**
 * Dispatch函数,用于处理reducer action的派发
 * @param {Object} fiber - 当前正在处理的Fiber节点
 * @param {Object} queue - 包含pending状态的队列
 * @param {*} action - 需要被处理的action
 */
function dispatchReducerAction(fiber, queue, action) {
  console.log('dispatch');
}

/**
 * 在mount期间为每个hook创建一个新的工作进度(work-in-progress)对象
 * @returns {Object} - 包含memoizedState, queue, next的hook对象
 */
function mountWorkInProgressHook() {
  const hook = {
    memoizedState: null,
    queue: null,
    next: null
  };
  if (workInProgressHook === null) {
    // 如果当前没有进行中的hook,设置新的hook为进行中的hook,并将其设置为当前渲染fiber的状态
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // 如果已经有进行中的hook,将新的hook添加到链表的最后,并将其设置为当前进行中的hook
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;  // 返回当前进行中的hook
}

/**
 * 用hooks渲染组件
 * @param {Object} current - 当前的Fiber节点
 * @param {Object} workInProgress - 正在进行的Fiber节点
 * @param {Function} Component - 需要渲染的组件
 * @param {Object} props - 组件的props
 * @returns {*} - 组件的子节点
 */
export function renderWithHooks(current, workInProgress, Component, props) {
  currentlyRenderingFiber = workInProgress;  // 设置当前正在渲染的fiber节点
  ReactCurrentDispatcher.current = HooksDispatcherOnMount;  // 设置当前的Dispatcher
  const children = Component(props);  // 通过组件和props渲染子节点
  return children;  // 返回子节点
}

基于 VitePress 构建