Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

模拟 React Hooks #9

Open
TopGrd opened this issue Jul 6, 2019 · 0 comments
Open

模拟 React Hooks #9

TopGrd opened this issue Jul 6, 2019 · 0 comments
Labels

Comments

@TopGrd
Copy link
Owner

TopGrd commented Jul 6, 2019

首先 我们先模拟一个useState

function useState(initVal) {
  let _val = initVal;
  const state = _val;
  function setState(newVal) {
    _val = newVal;
  }

  return [state, setState];
}

const [count, setCount] = useState(0);

console.log(count);

setCount(2);

console.log(count);

但是log 出来的count并不是我们所期望的 0 2,而是 0 0; 为什么呢? 因为在[count, setCount] = useState(0)count 复制的是 initVal, 修改 _val 的值, 而 count 仍然是initVal的值

如何让他恢复正常呢,我们可以使用方法来获取state

function useState(initVal) {
  let _val = initVal;
  const state = () => _val;
  function setState(newVal) {
    _val = newVal;
  }

  return [state, setState];
}

const [count, setCount] = useState(0);

console.log(count());

setCount(2);

console.log(count());

但是如果我们想真正模拟Hooks,就不能这样做。

我们可以用如下代码模拟 React, 主要使用闭包和数组模拟Hook,使用 hook[index] 存放 state 或者 依赖

// module pattern
const React = (function() {
  let hooks = []; // 存放所以使用的 hook
  let idx = 0; // hook 指针,来保证 hook 顺序的正常调用
  function useState(initVal) {
    const state = hooks[idx] || initVal;
    const _idx = idx; // _idx 确保 setState 修改的是同一个state 
    function setState(newVal) {
      hooks[_idx] = newVal;
    }

    idx++; // 如果再使用 useState, 指向 hook 数组的下一个

    return [state, setState];
  }

  function useEffect(callback, deps) {
    const oldDeps = hooks[idx];
    let hasChange = true;

    if (oldDeps) {
      hasChange = deps.some((dep, i) => !Object.is(dep, oldDeps[i]));
    }

    if (hasChange) callback();
    hooks[idx] = deps;
    idx++; // 如果再使用 useEffect, 指向 hook 数组的下一个
  }

  function render(Component) {
    const C = Component();
    C.render();
    idx = 0; // 每次 render 重置,确保 hooks 指针顺序
    return C;
  }

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

const { useState, useEffect, render } = React;

function Component() {
  const [count, setCount] = useState(0);
  const [type, setType] = useState("");

  useEffect(() => {
    console.log("jack");
  }, [count]);

  return {
    render: () => {
      document.body.innerHTML = "";
      const div = document.createElement("div");
      div.innerText = count;
      document.body.appendChild(div);
    },
    click: () => setCount(count + 1),
    type: () => setType("apple"),
    say: () => console.log(count, type)
  };
}

let App = render(Component);

setInterval(() => {
  App.click();
  App = render(Component); // 模拟 rerender
}, 1000);

效果看下面连接

mock react hooks

@TopGrd TopGrd added the react label Jul 10, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant