【问题标题】:Reactjs prevent unwanted renderings with useCallback and memoReactjs 使用 useCallback 和 memo 防止不需要的渲染
【发布时间】:2021-08-06 03:35:10
【问题描述】:

我有一个名为dataobjects 数组。我循环这个数组并渲染Counter 组件。计数器值的递增和递减作为 props 传递给组件。

但如果我更改一个组件中的值,其他两个组件也会重新渲染。这是不需要的。如何防止这种行为?我尝试了memouseCallback,但似乎没有正确实现。

Counter.js

import React, { useEffect } from "react";

    const Counter = ({ value, onDecrement, onIncrement, id }) => {
      useEffect(() => {
        console.log("Function updated!", id);
      }, [onDecrement, onIncrement]);
      return (
        <div>
          {value}
          <button onClick={() => onDecrement(id)}>-</button>
          <button onClick={() => onIncrement(id)}>+</button>
        </div>
      );
    };
    
    export default React.memo(Counter);

Home.js

import React, { useState, useCallback } from "react";
import Counter from "../components/Counter";

export default function Home() {
  const [data, setData] = useState([
    {
      id: 1,
      value: 0,
    },
    {
      id: 2,
      value: 0,
    },
    {
      id: 3,
      value: 0,
    },
  ]);

  const onIncrement = useCallback(
    (id) => {
      setData((e) =>
        e.map((record) => {
          if (record.id === id) {
            record.value += 1;
          }
          return record;
        })
      );
    },
    [data]
  );

  const onDecrement = useCallback(
    (id) => {
      setData((e) =>
        e.map((record) => {
          if (record.id === id) {
            record.value -= 1;
          }
          return record;
        })
      );
    },
    [data]
  );

  return (
    <div>
      <h1>Home</h1>
      {data.map((e) => {
        return (
          <Counter
            value={e.value}
            onDecrement={onDecrement}
            onIncrement={onIncrement}
            id={e.id}
          />
        );
      })}
    </div>
  );
}

【问题讨论】:

  • 添加 key 属性并移除 useCallback 依赖

标签: javascript reactjs react-hooks


【解决方案1】:

我怀疑 useCallbackuseMemo 在这种情况下没有帮助,因为您正在渲染中运行内联函数:

{data.map(e => <Counter ...>)}

此函数将始终返回一个新数组,并且组件将始终与前一个不同。

为了解决这个问题,我认为您应该记住该渲染函数,而不是 Counter 组件。

这是一个带有 useRef 的简单记忆渲染函数:


  // inside of a React component

  const cacheRef = useRef({})

  const renderCounters = (data) => {
    let results = []
    data.forEach(e => {
      const key = `${e.id}-${e.value}`
      const component = cacheRef.current[key] || <Counter
            value={e.value}
            key={e.id}
            onDecrement={onDecrement}
            onIncrement={onIncrement}
            id={e.id}
          />
      results.push(component)
      cacheRef.current[key] = component
    })

    return results
  }

  return (
    <div>
      <h1>Home</h1>
      {renderCounters(data)}
    </div>
  );

在下面的代码框里,只有被点击的组件会记录它的 id: https://codesandbox.io/s/vibrant-wildflower-0djo4?file=/src/App.js

免责声明:使用此实现,组件仅在其数据值更改时才会重新呈现。其他道具(例如递增/递减回调)不会触发更改。也没有办法清除缓存。

Memoize 也在用内存换取性能——有时这并不值得。如果可能有数千个 Counter,那么会有更好的优化,即更改 UI 设计、虚拟化列表等。

我确定有一种方法可以使用 useMemo/React.memo 执行此操作,但我不熟悉它

【讨论】:

    猜你喜欢
    • 2023-02-26
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 2019-05-29
    • 1970-01-01
    • 1970-01-01
    • 2016-07-26
    • 2021-04-20
    相关资源
    最近更新 更多