React-Hooks篇useEffect解析

587 阅读2分钟

1、useEffect使用方法

function AppPlayer(play) {
  const [num, setNum] = useState(0);
  let interval;
  useEffect(() => {
    interval = setInterval(() => {
      setNum(pre=> pre + 1)
      console.log('setInterval',num)
    }, 1000);
    return () => {
      clearInterval(interval);
      console.log("setInterval return");
    };
  }, []);
}
  • useEffect两个参数,第一个参数是一个方法,方法里面包含的是执行的逻辑,方法返回的是清理上一次渲染的逻辑。
  • 第二个参数,是代表执行的时机,当参数为空则每次渲染都会执行,当参数为空数组[],则只会在第一次渲染完执行一次,当参数不为空,则参数里面的值代表执行的一个依赖,只要这里面的参数有变化就会执行。

2、useEffect简单版实现

实现思路:

  • 全局状态prevEffectprevDeps 用于存储上一次执行的 effect 清理函数和依赖项。这不同于 React 的实际实现,React 会为每个组件实例和每个 hook 调用维护独立的状态。

  • useEffect 函数:

    1、接受一个副作用函数 effect 和一个依赖项数组 deps

    2、判断是否没有依赖项 hasNoDeps 或者依赖项是否发生变化 hasChangedDeps

    3、如果没有依赖项或依赖项发生了变化,首先调用上一次存储的 effect 清理函数(如果存在),然后执行当前的 effect 并存储其清理函数和依赖项。

  • 组件 MyComponent

    1、使用自定义的 useStateuseEffect

    2、useEffect 中的副作用函数会在 count 变化时执行,并返回一个清理函数。

  • 渲染过程:

    render 函数模拟 React 的渲染过程,每次状态更新都会重新调用 MyComponent 并执行副作用。

  • 模拟用户交互

    通过调用 setStateCallback 模拟用户点击按钮,更新状态并触发重新渲染和副作用。

// 存储上一个 effect 和依赖项的全局变量
let prevEffect;
let prevDeps;

// 自定义 useEffect 实现
function useEffect(effect, deps) {
  const hasNoDeps = !deps;
  const hasChangedDeps = prevDeps
    ? !deps.every((dep, i) => dep === prevDeps[i])
    : true;

  // 如果没有依赖项或者依赖项发生了变化
  if (hasNoDeps || hasChangedDeps) {
    // 如果存在上一个 effect 清理函数,先执行清理函数
    if (prevEffect) {
      prevEffect();
    }

    // 执行当前 effect,并将清理函数存储在 prevEffect
    prevEffect = effect();

    // 存储当前的依赖项
    prevDeps = deps;
  }
}

// 模拟一个简单的组件
function MyComponent() {
  // 模拟 useState
  const [count, setCount] = useState(0);

  // 使用自定义 useEffect
  useEffect(() => {
    console.log('Effect executed: Count is', count);

    // 返回一个清理函数
    return () => {
      console.log('Cleanup: Count was', count);
    };
  }, [count]);

  console.log('MyComponent Rendered with count:', count);

  // 模拟用户点击按钮来增加计数
  function handleClick() {
    setCount(count + 1);
  }

  return { handleClick };
}

// 模拟全局状态存储和渲染过程
let state;
let setStateCallback;

function useState(initialValue) {
  if (state === undefined) {
    state = initialValue;
  }

  function setState(newValue) {
    state = newValue;
    render();
  }

  return [state, setState];
}

function render() {
  const componentInstance = MyComponent();
  setStateCallback = componentInstance.handleClick;
}

// 初始渲染
render();

// 模拟用户交互
console.log('Initial state:', state);
setStateCallback();
console.log('State after one click:', state);
setStateCallback();
console.log('State after two clicks:', state);