用于地图渲染的 React useCallback 钩子

React useCallback hook for map rendering

将回调传递给组件时,我应该使用 useCallback 挂钩 return 记忆回调(以防止不必要的渲染):

import doSomething from "./doSomething";
const FrequentlyRerenders = ({ id }) => {
  const onEvent = useCallback(() => doSomething(id), [id]);
  return (
    <ExpensiveComponent onEvent={ onEvent } />
  );
};

但是如果我使用地图呢?例如:

import doSomething from "./doSomething";
const FrequentlyRerendersMap = ({ ids }) => {
  return ids.map(id => {
    const onEvent = useCallback(() => doSomething(id), [id]);
    return (
      <ExpensiveComponent key={id} onEvent={ onEvent } />
    );
  });
};

如何正确使用useCallback?以上是传递多个回调的正确方法吗?它真的有效并且知道根据数组的一项来记忆每个回调吗?

将返回的映射JSX转换为组件,然后就可以毫无问题地使用Callback

import doSomething from "./doSomething";
const MappedComponent =(props) => {
   const onEvent = useCallback(() => doSomething(props.id), []);
   return (
      <ExpensiveComponent onEvent={ onEvent } />
   );
}

const FrequentlyRerendersMap = ({ ids }) => {
  return ids.map(id => {
    return <MappedComponent key={id} id={id} />
  });
};

现在文档中明确不鼓励这样做。 https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

旧答案

重新架构的答案是回避 IMO 的问题。不过,我认为创建一个新组件可能是个好主意。

不过要回答这个问题,您的代码:

import doSomething from "./doSomething";
const FrequentlyRerendersMap = ({ ids }) => {
  return ids.map(id => {
    const onEvent = useCallback(() => doSomething(id), [id]);
    return (
      <ExpensiveComponent key={id} onEvent={ onEvent } />
    );
  });
};

这实际上是您想在地图中记忆的方法。我不知道 useCallback 的实现,但它应该增加很少的内存开销。 stackFrame,以及他们为将数组缩减为记忆函数的某种键所做的一切。

除非您使用大量元素来处理某些内容,否则 EG 会反应虚拟化组件无限滚动,否则您实际上可以安全地按自己的方式使用回调。事实上,与重新渲染所有这些组件相比,较小的内存开销可能要便宜得多。

还有一个选择:

import doSomething from "./doSomething";

const FrequentlyRerendersMap = ({ ids }) => {
  const onEvent = useCallback((id) => () => doSomething(id), []);

  return ids.map(id => {
    
    return (
      <ExpensiveComponent key={id} onEvent={ onEvent: onEvent(id) } />
    );
  });
};