模拟React Hooks之useEffect

108 阅读1分钟

模拟React.useEffect

const React = {
  hookState: [],
  stateIndex: 0,
  useState(initialValue) {
      // useState内部的模拟实现可以参考上一篇文章
      ...
  },
  useEffect(callback, nextStates) {
    // 记录当前state下标
    const cacheIndex = this.stateIndex;
    if (this.hookState[cacheIndex]) {
      // 如果存在历史值,就取值
      const [preUnmountFunc, preStates] = this.hookState[cacheIndex];
      if (!Array.isArray(preStates)) {
        // 如果第二个参数不为数组, 则每次都执行callback(这里简单模拟,实际), 用setTimeout模拟异步调用
        setTimeout(() => {
          if (typeof preUnmountFunc === 'function') preUnmountFunc();
          // useEffect有返回值, 是个函数, 作用相当于vue2的beforeDestroy
          const unmountFunc = callback();
          // 将返回值(函数)和states存起来
          this.hookState[cacheIndex] = [unmountFunc, nextStates]
        }, 0);
      } else {
        // 如果第二个参数是个数组, 则比较preStates 和 nextStates 的每一项, 都相等返回true
        let bol = preStates.every((preState, index) => {
          return nextStates[index] === preState;
        });
        if (!bol) {
          // 不是都相等, 则执行callback并将结果给stack赋值, 用setTimeout模拟异步调用
          setTimeout(() => {
            if (typeof preUnmountFunc === 'function') preUnmountFunc();
            const unmountFunc = callback();
            this.hookState[cacheIndex] = [unmountFunc, nextStates]
          }, 0);
        }
      }
    } else {
      // 如果不存在历史值, 执行callback并将结果给stack赋值, 用setTimeout模拟异步调用
      setTimeout(() => {
        const unmountFunc = callback();
        this.hookState[cacheIndex] = [unmountFunc, nextStates]
      }, 0);
    }
    // state的下标累加
    this.stateIndex++;
  },
};

模拟一个函数式组件

function functionComponent() {
  const [num1, setNum1] = React.useState(1);
  React.useEffect(() => {
    console.log('dom-挂载');
    setTimeout(() => {
      setNum1(100)
    }, 2000);
    return () => {
      console.log('组件卸载')
    }
  }, []);
  React.useEffect(() => {
    console.log('监听num1', num1)
    return () => {
      console.log('监听num1-卸载')
    }
  }, [num1]);
  console.log('function-render');
  return {
    num1,
    setNum1,
  }
};

模拟组件调用

var instance = functionComponent();
打印如下
// function-render
// dom-挂载
// 监听num1 1
隔2s后
// function-render
// 监听num1-卸载
// 监听num1 100