在 props 中传递引用类型是否会使 React.memo 无用?

Mic*_*ato 5 frontend memoization reactjs

假设我有一个PersonCard组件,它接收道具,并渲染一张显示人员信息的卡片。

interface PersonProps { 
    firstName: string,
    lastName: string
}

const PersonCard: React.FC<PersonProps> = (firstName, lastName) => {
    // render the things
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我要用来React.memo防止该组件不必要的重新渲染,它将如下所示:

const PersonCard: React.FC(<PersonProps>) => 
    React.memo(({firstName, lastName}) => {
    // render the things
    }
)
Run Code Online (Sandbox Code Playgroud)

据我了解,这是可行的:如果我的组件被使用相同的firstNameand调用两次lastName,它就不会重新渲染。


现在,当我们向组合中添加引用类型时,我的问题出现了:

interface PersonProps { 
    firstName: string,
    lastName: string,
    hobbies: Array<string>
}

const PersonCard: React.FC(<PersonProps>) => 
    React.memo(({firstName, lastName, hobbies}) => {
    // render the things
    }
)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,React.memo会进行浅比较(作为其默认行为),并且不会对hobbies数组起作用。

因此,即使和不改变,也PersonCard总是会重新渲染,因为它会认为发生了变化:这实际上与根本没有相同。firstNamelastNamehobbieshobbiesReact.memo

所以,我的问题是:我是否错了,或者将任何引用类型作为 prop 传递而不指定深度比较回调完全使 React.memo 的意义无效?

sup*_*610 4

一般来说,您是正确的:假设每个渲染之间的引用发生变化(通常是这种情况),将一个或多个引用类型作为道具传递给记忆组件将阻止记忆。为了唱反调,这里有几个场景,您不希望引用在每次渲染之间发生变化:

静态值

如果您将静态引用作为constprop 传递,您可以合理地预期它不会在渲染之间发生变化,并且应该被安全地记住。

const IDS = [1, 2, 3];

const App = () => (
  <MyMemoizedComponent ids={IDS} />
);
Run Code Online (Sandbox Code Playgroud)

这也适用于静态let变量,但需要注意的是,如果引用发生变化,记忆组件将重新呈现。

let IDS = [1, 2, 3];
IDS = IDS.slice(); // this would trigger a rerender
Run Code Online (Sandbox Code Playgroud)

参考文献

在使用有利于引用的组件时,通常会避免静态值。当使用您不希望 的值.current经常更改的 ref 时,您仍然可以获得记忆的好处。

const App = () => {
  const idsRef = React.useRef([1, 2, 3]);

  return (
    <MyMemoizedComponent ids={idsRef.current} />
  );
}
Run Code Online (Sandbox Code Playgroud)

使用备忘录

React.memo并且React.useMemo并不相互排斥!您可以使用 来记忆一个值,useMemo以防止它破坏记忆。

const App = () => {
  const idsMemo = React.useMemo(() => [1, 2, 3], []);

  return (
    <MyMemoizedComponent ids={idsMemo} />
  );
};
Run Code Online (Sandbox Code Playgroud)

这些示例有点人为,但您可以想象这样一个场景:数据未在本地初始化,而是通过网络获取,例如

const App = ({ path }) => {
  const ids = React.useMemo(() => fetchFromNetwork(path), [path]);

  return (
    <MyMemoizedComponent ids={ids} />
  );
};
Run Code Online (Sandbox Code Playgroud)

因此,总而言之,将一个或多个引用类型作为道具传递给记忆组件通常会破坏记忆。如果您希望引用类型频繁更改值(~每次渲染一次),只需删除记忆即可,这会导致额外的工作而没有任何好处。如果您不希望它经常更改,请记住它并仅在内部值更改时更新引用(或者,正如您所指出的,为 提供自定义深度相等比较函数React.memo)。