简单实现React-hook之useState

79 阅读2分钟
let isMount = true; // 执行阶段
let workInProgressHook = null; // 链表指针

// 模拟fiber对象,节点为App组件
const fiber = {
  stateNode: App,
  // 保存hooks数据 链表
  memoizedState: null,
};

// 调度方法 每次调度后重新渲染
function schedule() {
  // 每次重新渲染 hook 链表指针指向表头
  workInProgressHook = fiber.memoizedState;
  const app = fiber.stateNode(); // 调用节点重新渲染
  isMount = false; // 首次渲染是mount阶段, 之后为更新阶段

  return app;
}

// 模拟hooks #################################################################################################
function useState(initialState = null) {
  let hook; // 标识 识别对应的hook

  // 区分是否为首次渲染
  if (isMount) {
    // 首次渲染时 hooks 链表为null 初始化一个hook
    hook = {
      memoizedState: initialState,
      next: null, // 指针
      //   保存更改的状态 一个队列
      queue: {
        pending: null, // 保存的最后一个update
      },
    };
    // fiber上 memoizedState 不存在则使用创建的hook {初始渲染有多个hook时就会走else}
    if (!fiber.memoizedState) {
      fiber.memoizedState = hook;
      // 指针指向
      workInProgressHook = hook;
    } else {
      workInProgressHook.next = hook;
    }
  } else {
    // update情况
    hook = workInProgressHook; // 根据指针 取出对应hook
    workInProgressHook = workInProgressHook.next; // 操作后更改指针 指向下一个
  }

  // 基础状态
  let baseState = hook.memoizedState;
  // 根据queue 与 update 得到新的状态
  if (hook.queue.pending) {
    // 存在说明本次更新有新的update需要执行
    let firstUpdate = hook.queue.pending.next;

    // 遍历链表
    do {
      const action = firstUpdate.action; // 取出对应的action
      baseState = action(baseState); // 计算新的状态
      firstUpdate = firstUpdate.next; // 继续执行下一个
    } while (firstUpdate != hook.queue.pending.next);

    // 计算完成 清空链表
    hook.queue.pending = null;
  }

  hook.memoizedState = baseState;

  return [baseState, dispatchAction.bind(null, hook.queue)];
}

// 工具函数 派发更新操作 useSate 的第二个返回值
function dispatchAction(queue, action) {
  // hook为单向链表, 而update是环形链表(为了不同优先级执行顺序更加方便的操作)
  // update区分优先级
  const update = {
    action,
    next: null,
  };
  // 为null说明没有需要触发的更新
  if (queue.pending === null) {
    update.next = update;
  } else {
    // 环状链表尾插入 新的节点uodate 更改指针指向
    update.next = queue.pending.next;
    queue.pending.next = update;
  }
  // 保证ueue.pending 始终是链表的最后一个元素
  queue.pending = update;

  // 触发更新
  schedule();
}

// 模拟函数式组件
function App() {
  const [num, updateNum] = useState(0);
  const [num1, updateNum1] = useState(10);
  console.log(isMount);
  console.log(num);
  console.log(num1);
  return {
    onClick() {
      updateNum((num) => num + 1);
      updateNum1((num) => num + 1);
    },
  };
}

// 在函数中返回当前节点 (app组件) 保存到全局方便调用
// window.app = schedule();
let app = schedule();

setTimeout(app.onClick, 1000);