useMemo解析

447 阅读2分钟

useMemo

useMemo返回一个memoized值

代码示例
const memoInfo = useMemo(() => info, [info])
  • 把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
  • useMemo支持两个参数,第一个是创建的函数,第二个是依赖项数组,只有在依赖项数组发生变化时才会重新执行创建函数,用这种方法减少了渲染次数 🌰
const TestChild = memo(Test);
export default function App() {
  const [info, set] = useState({ name: "li", age: 12 });
  const change = (age) => {
    set({
      age: info.age + 1,
      name: info.name
    });
  };
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2
        onClick={() => {
          set({ name: info.name, age: info.age + 1 });
        }}
      >
        age: {info.age}
        name: {info.name}
      </h2>
      <TestChild
        info={useMemo(() => info, [info.name])}
      />
      <TestChild
        info={info}
      />
    </div>
  );
}
import React from "react";

const Test = (props) => {
  console.log("render Test");
  const onClick = () => {
    props.onchage(props.info.age + 1);
  };
  return (
    <div onClick={() => onClick()}>
      <p>{props.info.age}</p>
    </div>
  );
};
export default Test;

代码中有两处引用了Test,区别为前者的参数是用useMemo包裹住的,后者没有;我们在点击h2标签的onClick方法时查看Test组件的渲染次数

image.png 结果只有一次,说明用useMemo包裹的参数并没有发生变化,所以第一个Test组件没有发生重新渲染 useMemo

那么问题来了,useMemo是怎么实现这个功能的?

我们首先找到react的源码

// https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberHooks.new.js
//创建useMemo
function mountMemo<T>(
  nextCreate: () => T,
  deps: Array<mixed> | void | null,
): T {
  //初始化hook
  const hook = mountWorkInProgressHook();
  //拿到依赖项
  const nextDeps = deps === undefined ? null : deps;
  // 生成useMemo的返回值
  const nextValue = nextCreate();
  // 缓存返回值和当前依赖项
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}
//更新useMemo
function updateMemo<T>(
  nextCreate: () => T,
  deps: Array<mixed> | void | null,
): T {
  // 拿到当前的hook
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  // 取出hook的缓存值
  const prevState = hook.memoizedState;
  //对比当前依赖值和已经缓存值是否相等如果相同,返回原值否则返回新的对象
  if (prevState !== null) {
    // Assume these are defined. If they're not, areHookInputsEqual will warn.
    if (nextDeps !== null) {
      const prevDeps: Array<mixed> | null = prevState[1];
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        return prevState[0];
      }
    }
  }
  const nextValue = nextCreate();
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}

useMemo又是如何对比新旧值呢?

答案就是Object.is() 感兴趣的话可以研究一下源码,方法areHookInputsEqual()