深入了解react hooks原理Ⅰ 实现了useState,遗留了两个问题

  • 使用多个 useState 是否能够符合预期
  • useEffect 是怎么实现的

使用 useState 是否能够符合预期

我们新增一个状态

function Component() {
  const [count, setCount] = React.useState(1);
  const [text, setText] = React.useState("hello");
  return {
    render: () =>
      console.log({
        count,
        text
      }),
    click: () => setCount(count + 1),
    say: (text) => setText(text)
  };
}

可以看到,每次render的结果跟我们预期的不一致,每次 counttext 都是最后赋值的结果,这时候可以通过数组来存储 useState,我们改写下React模块,新增一个 hooks 数组来存储state的值

const React = (function () {
  let hooks = []; // 定义一个hooks数组存储值
  let idx = 0;

  function useState(initVal) {
    const state = hooks[idx] || initVal;
    const _idx = idx; // 保留首次useState下标,通过下标可以记录当前state的位置
    const setState = (newVal) => {
      hooks[_idx] = newVal;
    };
    idx++;
    return [state, setState];
  }

  function render(Component) {
    const C = new Component();
    C.render();
    idx = 0; // 每次render完成后,下标置0
    return C;
  }

  return {
    useState,
    render
  };
})();

function Component() {
  const [count, setCount] = React.useState(1);
  const [text, setText] = React.useState("hello");
  return {
    render: () =>
      console.log({
        count,
        text
      }),
    click: () => setCount(count + 1),
    say: (text) => setText(text)
  };
}

这样结果就一致了

useEffect实现

我们先来看看 useEffect 的语法

useEffect(callback, deps);

第一个参数是回调 callback ,第二个参数是依赖的数组 deps 

回到上面的 useState 实现,我们通过新增 hooks 数组存储 useState 的值,也可以继续通过 hooks 数组来存储依赖的值,也是我们可以实现一个 useEffect

function useEffect(cb, deps) {
  // 旧的依赖值,这里的依赖是存放在hooks的所有state后面
  const oldDeps = hooks[idx];
  let hasChanged = true;
  if (oldDeps) {
    // 通过 Object.is 来判断依赖是否改变
    hasChanged = deps.some((val, i) => !Object.is(val, oldDeps[i]));
  }
  if (hasChanged) cb();
  hooks[idx] = deps;
  idx++;
}

最终代码以及效果