likes
comments
collection
share

为何你的 React.memo 没有生效?useMemo、useCallback用起来!

作者站长头像
站长
· 阅读数 13

当在开发 React 应用程序时,优化性能是一个重要的考虑因素。React 提供了一些优化工具和技术,其中之一就是 React.memoReact.memo 是一个高阶组件(Higher-Order Component,HOC),用于优化函数组件的渲染性能。

什么是 React.memo

React.memo 是 React 提供的一个函数,用于优化函数组件的渲染性能。它类似于 React.PureComponent,但适用于函数组件而不是类组件。React.memo 函数接收一个组件作为参数,并返回一个经过优化的新组件。

优化的原理是 React.memo 对组件的输入 props 进行浅比较。它会比较当前渲染时的 props 和前一次渲染时的 props 是否相等。只有当 props 发生变化时,React.memo 才会重新渲染组件;否则,它会使用前一次渲染的结果,从而避免不必要的重渲染。

如何使用

import React from 'react';

const UserProfile = React.memo(({ name, age, avatar }) => {
  console.log('Rendering UserProfile');
  
  return (
    <div>
      <img src={avatar} alt="User Avatar" />
      <div>Name: {name}</div>
      <div>Age: {age}</div>
    </div>
  );
});

在这个示例中,UserProfile 是一个纯展示组件,它接收用户的姓名、年龄和头像作为 props。由于它的渲染结果只依赖于传入的 props,我们可以使用 React.memo 来优化它。这样,只有当 nameageavatar 发生变化时,才会重新渲染 UserProfile 组件。

为何你的React.memo没有生效

1. 直接在函数式组件顶层定义引用类型:

如下的代码中,每次count被更新,App都会重新渲染,obj对象和onChanged函数也会被重新声明,所以对于 MyComponent 来说 obj一直在发生变化,导致子组件一直在重复渲染

import React from 'react';

const MyComponent = React.memo((props) => {
  console.log('Rendering MyComponent');
  return <div>{props.name}</div>;
});

function App() {
  const [count, setCount] = React.useState(0);

  const handleClick = () => {
    setCount(count + 1);
  };

  const onChanged = ()=>{}

  const obj = { value: count };

  return (
    <div>
      <button onClick={handleClick}>Increment Count</button>
      <MyComponent name="John" obj={obj} onChanged={onChanged}/>
    </div>
  );
}

如果要避免重复渲染,需要保持引用不变:

// 缓存对象
const obj = useMemo(() => ({ value: 1 }), []);

// 缓存函数
const onChanged = useCallback(() => {}, []);

2. 直接setState一个新的对象:

如下的代码中,每次执行setObj方法都会被传入一个新对象,它的引用地址发生了变化,所以子组件会重新渲染

import React from 'react';

const MyComponent = React.memo((props) => {
  console.log('Rendering MyComponent');
  return <div>{props.name}</div>;
});

function App() {
  const [obj, setObj] = React.useState({});

  const handleClick = () => {
    setObj({ a: 3 });
  };

  return (
    <div>
      <button onClick={handleClick}>Increment Count</button>
      <MyComponent name="John" obj={obj} />
    </div>
  );
}

如果不想重新渲染,需要保持引用地址不变:

    // 直接更改旧值
    setObj((old) => {
      old.a = 3;
      return old;
    });
    
    // 缓存对象,仅在发生变化时重新创建新对象,即触发重新渲染
    const newObj = useMemo(() => ({ value: count }), [count]);
    setObj(newObj)

请注意,React.memo 的比较是浅比较,只比较 props 对象的第一层属性是否发生变化。如果 props 包含复杂的数据结构(如对象或数组),并且其引用没有发生变化,那么 React.memo 无法检测到深层的变化。在这种情况下,需要使用其他方法来确保组件的正确更新。

总之,除了基本类型变化外,你还需要关注你传递的引用类型数据是否发生了变化,从而去判断你的组件是否会被重新渲染。

结论

通过使用 React.memo,我们可以优化函数组件的渲染性能,避免不必要的重渲染。它通过浅比较 props 对象的方式来决定是否重新渲染组件。然而,需要注意的是,React.memo 的比较是浅比较,只检查第一层属性是否发生变化。

在实际开发中,我们应该根据组件的特性和需求,合理选择使用 React.memo 进行优化。对于纯展示组件和具有大量子组件的组件树,使用 React.memo 可以有效地提升性能和减少不必要的渲染。

希望本文能够帮助你理解和应用 React.memo,优化你的 React 应用程序的性能!