提示:本文主要讲解useReducer
Fiber是一种数据结构,用于描述虚拟DOM。
react目前的做法是使用链表。每个virtualDOM节点内部表示为一个Fiber
Fiber是一个执行单元,每次执行完一个执行单元, React 就会检查现在还剩多少时间,如果没有时间就将控制权让出去
循环链表是另一种形式的链式存储结构
它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环
思考:这种结构该如何实现???
代码如下(示例):
function enqueueUpdate(queue, action){
const update = {action, next:null}
//TODO ?
}
Hook是React 16.8的新增特性。它可以让你在不便携class的情况下使用state以及其他的React特性。
官方给出的动机是解决长时间使用和维护react过程中经常遇到的问题。
1、在组件之间复用状态逻辑很难(比如:render、props和高阶组件)
2、复杂组件变得难以理解(比如:不想干的逻辑放在一起)
3、难以理解的class(比如:this的工作方式)
用法:略
// 调度更新的方法
// import { scheduleUpdateOnFiber } from 'ReactFiberWorkLoop';
// ReactCurrentDispatcher是暴露给用户调用hook的接口,兼容mount和update时,调用相同hook,实现不同逻辑。(面向接口编程)
/**
*
interface IReactCurrentDispatcherValue {
useReducer: (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 && 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和useReducer是不是有相同的点?那怎么实现呐。。。
期待更多。。。