React Hooks原理

useState实现

function useState(initialValue) {
  var _val = initialValue;

  function state(){
    return _val;
  }

  function setState(newVal){
    _val = newVal;
  }

  return [state, setState];
}

使用

var [foo, setFoo] = useState(0) // 数组解构
console.log(foo()); //output: 0

setFoo(1);
console.log(foo()); //output: 1

在函数组件中使用

function Counter() {
  const [count, setCount] = useState(0) // 和上文实现的一样
  return {
    click: () => setCount(count() + 1),
    render: () => console.log('render:', { count: count() })
  }
}

const C = Counter()
C.render() // render: { count: 0 }
C.click()
C.render() // render: { count: 1 }

React中的state需要设计为一个变量而不是一个函数

1、有bug版

function useState(initialValue) {
  var _val = initialValue;

  function setState(newVal){
    _val = newVal;
  }

  return [_val, setState];
}

var [foo, setFoo] = useState(0);
console.log(foo); //output: 0

setFoo(1);
console.log(foo); //output: 0

2、模块内部的闭包

var MyReact = (function(){
  let _val;

  return {
    render(Component) {
      const Comp = Component();
      Comp.render();
      return Comp;
    },
    useState(initialValue) {
      _val = _val || initialValue;
      function setState(newVal) {
        _val = newVal;
      }
      return [_val, setState];
    }
  }
})();

function Counter() {
  const [count, setCount] = MyReact.useState(0) // 和上文实现的一样
  return {
    click: () => setCount(count + 1),
    render: () => console.log('render:', { count: count })
  }
}

let App; 
App = MyReact.render(Counter); // render: { count: 0 }
App.click();
App = MyReact.render(Counter); // render: { count: 1 }

实现useEffect

var MyReact = (function(){
  let _val, _deps;

  return {
    render(Component) {
      const Comp = Component();
      Comp.render();
      return Comp;
    },
    useEffect(callback, depArray){
      const hasNoDeps = !depArray; //没有依赖项目
      const hasChangedDeps = _deps ? !depArray.every((el,i) => el === _deps[i]) : true; //是否有改变的依赖项目
      if(hasNoDeps || hasChangedDeps){
        callback();
        _deps = depArray;
      }
    },
    useState(initialValue) {
      _val = _val || initialValue;
      function setState(newVal) {
        _val = newVal;
      }
      return [_val, setState];
    }
  }
})();

使用

function Counter() {
  const [count, setCount] = MyReact.useState(0)
  MyReact.useEffect(() => {
    console.log('effect', count)
  }, [count])
  return {
    click: () => setCount(count + 1),
    noop: () => setCount(count),
    render: () => console.log('render', { count })
  }
}
let App
App = MyReact.render(Counter)
// effect 0
// render {count: 0}

App.click()
App = MyReact.render(Counter)
// effect 1
// render {count: 1}

App.noop()
App = MyReact.render(Counter)
// render {count: 1}

App.click()
App = MyReact.render(Counter)
// effect 2
// render {count: 2}

接收任意数量的state和effect

var MyReact = (function () {
  let hooks = [],
    currentHook = 0; // hooks数组 和 当前hook的索引
  return {
    render(Component) {
      const Comp = Component(); // 执行 effects
      Comp.render();
      currentHook = 0; // 为下一次render重置hook索引
      return Comp;
    },
    useEffect(callback, depArray) {
      const hasNoDeps = !depArray;
      const deps = hooks[currentHook]; // type: array | undefined
      const hasChangedDeps = deps
        ? !depArray.every((el, i) => el === deps[i])
        : true;
      if (hasNoDeps || hasChangedDeps) {
        callback();
        hooks[currentHook] = depArray;
      }
      currentHook++; // 当前hook处理完毕
    },
    useState(initialValue) {
      hooks[currentHook] = hooks[currentHook] || initialValue; // type: any
      const setStateHookIndex = currentHook; // 为了setState引用正确的闭包
      const setState = (newState) => (hooks[setStateHookIndex] = newState);
      return [hooks[currentHook++], setState];
    },
  };
})();

使用:

function Counter() {
  const [count, setCount] = MyReact.useState(0)
  const [text, setText] = MyReact.useState('foo') // 第二个 state hook!
  MyReact.useEffect(() => {
    console.log('effect', count, text)
  }, [count, text])
  return {
    click: () => setCount(count + 1),
    type: txt => setText(txt),
    noop: () => setCount(count),
    render: () => console.log('render', { count, text })
  }
}
let App
App = MyReact.render(Counter)
// effect 0 foo
// render {count: 0, text: 'foo'}

App.click()
App = MyReact.render(Counter)
// effect 1 foo
// render {count: 1, text: 'foo'}

App.type('bar')
App = MyReact.render(Counter)
// effect 1 bar
// render {count: 1, text: 'bar'}

App.noop()
App = MyReact.render(Counter)
// // 没有effect执行
// render {count: 1, text: 'bar'}

App.click()
App = MyReact.render(Counter)
// effect 2 bar
// render {count: 2, text: 'bar'}

自定义hook

function useSplitURL(str) {
  const [text, setText] = MyReact.useState(str)
  const masked = text.split('.')
  return [masked, setText]
}
function Component() {
  const [text, setText] = useSplitURL('www.netlify.com')
  return {
    type: txt => setText(txt),
    render: () => console.log({ text })
  }
}
let App
App = MyReact.render(Component)
// { text: [ 'www', 'netlify', 'com' ] }

App.type('www.reactjs.org')
App = MyReact.render(Component)
// { text: [ 'www', 'reactjs', 'org' ] }}

More

29行代码深入React Hooks原理
https://juejin.cn/post/6844904128326434823

Deep dive: How do React hooks really work?
https://www.netlify.com/blog/2019/03/11/deep-dive-how-do-react-hooks-really-work/

ReactHooks原理及简单实现
https://www.shymean.com/article/ReactHooks%E5%8E%9F%E7%90%86%E5%8F%8A%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0