site infoHacknerd | Tech Blog
blog cover

👟 [React源码解析] 15. 手写Hooks

JavaScriptReact
image
javascriptCopy
import React from "react";
import ReactDOM from "react-dom";

let isMount = true;
let workInProgressHook;

const fiber = {
  memoizedState: null, // hook链表
  stateNode: App, // dom
};

function mountWorkInProgressHook() {
  const hook = {
    memoizedState: null,
    quene: {
      pending: null, // 未执行的update
    },
    next: null, // 下一个hook
  };

  if (!fiber.memoizedState) {
    fiber.memoizedState = hook;
  } else {
    // 上一个链表加上hook
    workInProgressHook.next = hook;
  }

  workInProgressHook = hook;
  return workInProgressHook;
}

function updateWorkInProgressHook() {
  let currentGook = workInProgressHook;
  workInProgressHook = workInProgressHook.next;
  return currentGook;
}

function useState(initialState) {
  let hook;
  if (isMount) {
    hook = mountWorkInProgressHook();
    hook.memoizedState = initialState;
  } else {
    hook = updateWorkInProgressHook();
  }

  let baseState = hook.memoizedState;
  if (hook.quene.pending) {
    let fristUpdate = hook.quene.pending.next;

    do {
      const action = fristUpdate.action;
      baseState = action(baseState);
      fristUpdate = fristUpdate.next;
    } while (fristUpdate !== hook.quene.pending);

    hook.quene.pending = null;
  }

  hook.memoizedState = baseState;

  return [baseState, dispacthAction.bind(null, hook.quene)];
}

function dispacthAction(quene, action) {
  const update = {
    action,
    next: null,
  };

  if (quene.pending === null) {
    update.next = update;
  } else {
    update.next = quene.pending.next;
    quene.pending.next = update;
  }

  quene.pending = update;
  isMount = false;
  workInProgressHook = fiber.memoizedState;
  schudule();
}

const Dispatcher = {
  useState,
};

function App() {
  const [age, addAge] = Dispatcher.useState(10);
  const [count, addcount] = Dispatcher.useState(0);

  return (
    <>
      <p>{age}</p>
      <button onClick={() => addAge((age) => age + 1)}> add age </button>
      <p>{count}</p>
      <button onClick={() => addcount((count) => count + 1)}> add count </button>
    </>
  );
}

function schudule() {
  ReactDOM.render(<App></App>, document.getElementById("root"));
}

schudule();

Contents


    2024/04/23 09:22