React Memo

组件一多,「重复渲染」就成了性能杀手:父组件点一下按钮,所有子组件都跟着重新执行一遍,哪怕它们的数据根本没变。React 为此提供了 memo、useMemo、useCallback 三件套,相当于给渲染加缓存。本节讲清各自的职责与适用场景。

问题:父组件一更新,子组件全跟着跑

默认情况下,父组件的 state 变化触发自身重渲染时,子组件也会无条件重渲染。

import { useState } from 'react';

function Child({ name }) {
  console.log('Child 执行了');
  return <p>{name}</p>;
}

export default function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <button onClick={() => setCount(count + 1)}>计数:{count}</button>
      <Child name="小明" />
    </>
  );
}
// 每次点击按钮,控制台都会打印:Child 执行了

React.memo:props 没变就跳过渲染

用 memo 包裹组件后,React 会先浅比较新旧 props,发现没变化就直接复用上次渲染结果,跳过整个渲染过程。

import { memo, useState } from 'react';

const Child = memo(function Child({ name }) {
  console.log('Child 执行了');
  return <p>{name}</p>;
});
// name 不变时再点按钮,控制台不再打印

注意:memo 只做浅比较。父组件每次渲染都新建对象或函数时,它们的引用每次都不同,memo 依然会失效,需要配合 useCallback、useMemo 稳定引用。

useMemo:缓存计算结果

函数组件每次渲染都会重新执行函数体,昂贵的计算应交给 useMemo,只在依赖变化时才重算。

import { useMemo } from 'react';

const count = useMemo(() => {
  return list.filter((item) => item.price > 100).length;
}, [list]); // list 不变,结果直接复用上次的

useCallback:缓存函数引用

父组件每次渲染都会生成一个新函数,即使函数体完全一样。传给 memo 子组件的函数若用 useCallback 固定引用,memo 才能真正生效。

import { useCallback } from 'react';

const handleClick = useCallback(() => {
  console.log('点击了文章', id);
}, [id]); // id 不变时,handleClick 始终是同一个引用

小结

三件套的职责一句话就能记住:memo 缓存组件,useMemo 缓存计算值,useCallback 缓存函数。它们都只为避免无谓渲染服务,自身也有比较开销,先确认渲染确实昂贵再使用,才是正确的打开方式。

笔记加载中…