【问题标题】:React useCallback hook for map renderingReact 用于地图渲染的 useCallback 钩子
【发布时间】:2019-09-21 15:39:29
【问题描述】:

当向组件传递回调时,我应该使用useCallback 钩子来返回一个记忆化的回调(以防止不需要的渲染):

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?以上是传递多个回调的正确方法吗?它真的有效并且知道根据数组的一项来记忆每个回调吗?

【问题讨论】:

    标签: reactjs react-hooks usecallback


    【解决方案1】:

    将返回的映射后的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} />
      });
    };
    

    【讨论】:

    • 我想过,但由于系统架构的原因,我不能。有没有办法在不创建新组件的情况下修复它?
    • 如何改变系统架构。您创建一个组件以优化性能。处理它的另一种方法是在 ExpensiveComponent 中进行更改,从而将 id 作为道具与 doSomething 一起传递给它,并从 ExpensiveComponent 中调用它
    • 就此而言,您可以在MappedComponent 上使用memo 并完全避免使用useCallback()
    【解决方案2】:

    另一种选择:

    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) } />
        );
      });
    };
    

    【讨论】:

    • onEvent 每次渲染都会不同。
    【解决方案3】:

    现在文档中明确不鼓励这样做。 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 反应虚拟化组件无限滚动,否则您实际上可以安全地使用 Callback 的方式。事实上,较小的内存开销可能比重新渲染所有这些组件要便宜得多。

    【讨论】:

    • 我不确定如果列表发生变化会有什么行为。
    • 如果你在依赖数组中传递一个不同的id,它应该返回一个不同的函数。
    • 这给了我一个错误:React Hook "useCallback" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
    • @Tom 可能是它随着反应版本而改变了?或者你有这个逻辑嵌套在 useCallback 中?
    • 尽管reactjs.org/docs/… 文档中并未明确不鼓励这种模式
    猜你喜欢
    • 2020-01-09
    • 2022-08-19
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多