React.memo 是 React 提供的 高阶组件(HOC),用于优化函数组件的性能。它通过浅比较(shallow comparison)props,只有当 props 发生变化时才会重新渲染组件,从而减少不必要的重新渲染,提高应用的性能。


目录

  1. React.memo 的基本用法
  2. React.memo 的工作原理
  3. React.memo 的应用场景
  4. React.memo 与 useCallback
  5. React.memo 的局限性
  6. 参考资料

1. React.memo 的基本用法

使用 React.memo 只需将函数组件传递给 React.memo(),它会返回一个优化后的组件

示例:使用 React.memo

import React from 'react';

// 普通函数组件
function MyComponent({ name }) {
  console.log('MyComponent 渲染');
  return <h1>你好, {name}!</h1>;
}

// 使用 React.memo 进行优化
export default React.memo(MyComponent);

解释:

  • React.memo(MyComponent):创建一个**记忆化(memoized)**组件,只有 props.name 发生变化时才会重新渲染。
  • 如果 props.name 没有变化,组件不会重新渲染,控制台不会打印 "MyComponent 渲染"

测试 React.memo 效果

我们用一个父组件来测试 MyComponent 是否会重新渲染:

import React, { useState } from 'react';
import MyComponent from './MyComponent';

function ParentComponent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>增加计数: {count}</button>
      <MyComponent name="张三" />
    </div>
  );
}

export default ParentComponent;

运行结果:

  • 点击按钮增加 count 时,ParentComponent 会重新渲染,但 MyComponent 不会重新渲染(因为 name="张三" 没有变)。
  • 如果 MyComponent 未使用 React.memo,它会随 ParentComponent 一起重新渲染。

2. React.memo 的工作原理

React.memo 使用浅比较(shallow comparison)来检查 props 是否变化:

  • 基本数据类型(string、number、boolean):直接比较值是否相等。
  • 引用类型(对象、数组、函数):仅比较引用是否相同,而不会深度比较其内容。

示例:浅比较导致的问题

import React from 'react';

const MyComponent = React.memo(({ data }) => {
  console.log('MyComponent 渲染');
  return <p>{data.value}</p>;
});

export default function App() {
  const obj = { value: 'Hello' };

  return <MyComponent data={obj} />;
}

问题

  • 每次 App 重新渲染时,obj 都是新的引用,即使 value 没变,React.memo 仍然会重新渲染 MyComponent

解决方案: 使用 useMemo 保持 data 引用不变:

import React, { useMemo } from 'react';

export default function App() {
  const obj = useMemo(() => ({ value: 'Hello' }), []);

  return <MyComponent data={obj} />;
}


3. React.memo 的应用场景

适用场景

子组件渲染与父组件状态无关

  • 组件 props 不变,但因父组件重新渲染而被迫更新时,使用 React.memo 避免不必要的渲染。

列表项

  • React.memo长列表组件(如 map 渲染的列表项)中非常有用,可以减少渲染开销。

性能优化

  • 适用于高频次渲染的组件(如搜索框、实时更新数据等)。

4. React.memo 与 useCallback

如果 props 传递的是函数React.memo 不会阻止子组件重新渲染,因为函数是引用类型,每次渲染都会创建新函数。

示例:父组件导致子组件重复渲染

import React, { useState } from 'react';

const ChildComponent = React.memo(({ onClick }) => {
  console.log('ChildComponent 渲染');
  return <button onClick={onClick}>点击</button>;
});

function ParentComponent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>增加计数: {count}</button>
      <ChildComponent onClick={() => console.log('Clicked!')} />
    </div>
  );
}

export default ParentComponent;

结果:

  • 每次 ParentComponent 重新渲染时,ChildComponent 都会重新渲染,因为 onClick={() => console.log('Clicked!')}新函数,导致 props 变化。

解决方案:使用 useCallback

useCallbackonClick 保持相同引用,避免子组件重新渲染:

import React, { useState, useCallback } from 'react';

const ChildComponent = React.memo(({ onClick }) => {
  console.log('ChildComponent 渲染');
  return <button onClick={onClick}>点击</button>;
});

function ParentComponent() {
  const [count, setCount] = useState(0);
  
  // 使用 useCallback 让 onClick 的引用保持不变
  const handleClick = useCallback(() => {
    console.log('Clicked!');
  }, []);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>增加计数: {count}</button>
      <ChildComponent onClick={handleClick} />
    </div>
  );
}

export default ParentComponent;

结果:

  • ChildComponent 不会重复渲染,因为 handleClick 只在组件挂载时创建一次,避免 props 变化。

5. React.memo 的局限性

只针对函数组件生效,类组件不能使用 React.memo
默认只做浅比较,深层次对象或数组变化时需要 useMemouseCallback 处理。
useStateuseEffect 一起使用时要注意,因为 state 更新仍会触发 re-render
不适用于所有组件,如果组件本身渲染代价很低,使用 React.memo 可能反而会增加性能开销。


6. 参考资料

出站链接

站内链接


总结

React.memo 用于优化函数组件,防止不必要的重新渲染
浅比较 props,只有 props 变化时才会重新渲染。
useCallbackuseMemo 结合使用,防止引用类型 props 造成无效渲染。
适用于高性能优化场景,但不适用于所有组件,需要结合实际需求使用。