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 ReactSharedInternalspackages/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; // 返回子节点
}