hook原理

hook的使用,实现了useState,useReduce,useEffect
<div id="app"></div>
代码
let currentComponent;
let currentIndex;
const components = [];

class Component {
  __hooks = {
    _hookList: [],
    _effectHookList: [],
    _cleanEffectHookList: [],
    _layoutHookList: [],
  };
  diff() {}
  // 更新函数
  update() {
    this.render();
  }
  beforeRender() {
    currentIndex = 0;
    currentComponent = this;
  }
  afterRender() {
    this.__hooks._cleanEffectHookList.forEach((fn) => {
      fn();
    });
    this.__hooks._cleanEffectHookList = [];
    this.__hooks._effectHookList.forEach((fn) => {
      const clean = fn();
      if (typeof clean === "function") {
        this.__hooks._cleanEffectHookList.push(clean);
      }
    });
    this.__hooks._effectHookList = [];
  }

  afterDomRender() {
    this.__hooks._layoutHookList.forEach((fn) => {
      fn();
    });
    this.__hooks._layoutHookList = [];
  }
  render() {
    this.beforeRender();
    const [state, setState] = useState(0);

    useEffect(() => {
      console.log("useEffect mount");
      return () => {
        console.log("useEffect clear");
      };
    }, [state]);

    useLayoutEffect(() => {
      console.log("useLayout Render");
    }, []);

    this.afterRender();

    const dom = document.getElementById("app");
    let text;
    if (!(text = document.getElementById("text"))) {
      text = document.createElement("h1");
      text.id = "text";
      dom.appendChild(text);
    }
    let button;
    if (!(button = document.getElementById("button"))) {
      button = document.createElement("button");
      button.id = "button";
      button.textContent = "add";
      dom.appendChild(button);
    }
    let subButton;
    if (!(subButton = document.getElementById("button1"))) {
      subButton = document.createElement("button");
      subButton.id = "button1";
      subButton.textContent = "sub";
      dom.appendChild(subButton);
    }
    subButton.onclick = () => {
      setState(state - 1);
    };
    button.onclick = () => {
      setState(state + 1);
    };
    text.textContent = state;

    this.afterDomRender();
  }
}
new Component().render();

function getHookState(index) {
  const hooks = currentComponent.__hooks;

  // 加入的
  // []
  // [useState,useState,useState]...
  if (index >= hooks._hookList.length) {
    // 每种类型的hook的属性是不同的
    hooks._hookList.push({});
  }
  return hooks._hookList[index];
}

function useState(initState) {
  return useReducer((initState, action) => action, initState);
}

function useReducer(reducer, initState) {
  const hookState = getHookState(currentIndex++);
  // hook 是否初始化
  if (!hookState.__value) {
    hookState.__value = [
      // value
      initState,
      // dispatch
      (action) => {
        const nextValue = reducer(hookState.__value[0], action);
        hookState.__value[0] = nextValue;
        hookState._component.update();
      },
    ];
    if (!hookState._component) {
      hookState._component = currentComponent;
    }
  }

  return hookState.__value;
}
function useEffect(cb, deps) {
  const hookState = getHookState(currentIndex++);

  if (!hookState._value || isChange(deps, hookState.__value[1])) {
    hookState.__value = [];
    hookState.__value[0] = cb;
    hookState.__value[1] = deps;
    currentComponent.__hooks._effectHookList.push(hookState.__value[0]);
  }
}
function useLayoutEffect(cb, deps) {
  const hookState = getHookState(currentIndex++);

  if (!hookState._value || isChange(deps, hookState.__value[1])) {
    hookState.__value = [];
    hookState.__value[0] = cb;
    hookState.__value[1] = deps;
    currentComponent.__hooks._layoutHookList.push(hookState.__value[0]);
  }
}

function isChange(oldArgs, newArgs) {
  return (
    !oldArgs ||
    oldArgs.length !== newArgs.length ||
    newArgs.some((arg, index) => arg !== oldArgs[index])
  );
}