反应如何记忆一个组件,该组件接收一个非常嵌套的 object/array 作为 prop

React how to memoize a component that receives a very nested object/array as prop

React.memo 使用浅比较来确定 props 是否相等,但我需要传递一个对象或数组作为 prop,所以我进入了 areEqual 条件,但是 currentPropsnextProps 值始终相同。我的意思是,该组件不呈现。

这么说吧:

export default function App() {
  const [data, setData] = useState([
    {
      name: "First name",
      amount: 0
    },
    {
      name: "Other name",
      amount: 0
    }
  ]);
  const [value, setValue] = useState("");

  return (
    <>
      <input
        type="text"
        placeholder="Type in for test"
        value={value}
        onChange={(e) => {
          setValue(e.target.value);
        }}
      />
      <br />
      <input
        type="button"
        value="Click to increment first!"
        onClick={() => {
          const temp = [...data];
          temp[0].amount += 1;
          setData(temp);
        }}
      />
      <input
        type="button"
        value="Click to increment other!"
        onClick={() => {
          const temp = [...data];
          temp[1].amount += 1;
          setData(temp);
        }}
      />
      <br />

      <Child data={data} />
    </>
  );
}

const Child = ({ data }) => {
  const count = useRef(0);
  return (
    <>
      {data &&
        data.map((obj, index) => {
          return obj.name + "-" + obj.amount;
        })}
      <br />
      Count: {count.current++}
    </>
  );
};

const areEqual = (currentProps, nextProps) => {
  console.log(currentProps.data[0].amount, nextProps.data[0].amount);
  console.log(currentProps.data[1].amount, nextProps.data[1].amount);
  if (
    currentProps.data[0].amount === nextProps.data[0].amount &&
    currentProps.data[1].amount === nextProps.data[1].amount
  ) {
    return true;
  }
  return false;
};

export default memo(Child, areEqual);

但无论 currentProps 和 nextProps 总是返回相同的值:

一切都在 this sandbox。我在这里错过了什么?

问题出在对象突变上。而是创建一个新的对象引用。

不要

const temp = [...data];
temp[0].amount += 1;
setData(temp);

setData(
  data.map((item, index) =>
    index === 0 ? { ...item, amount: item.amount + 1 } : item
  )
);

也有效(使用临时变量)

如果您更喜欢使用 temp 变量的变异样式,则应避免使用相同的对象引用:

const temp = [...data];
temp[0] = { ...temp[0], amount: temp[0].amount + 1 };
setData(temp);

为什么?

but no matter what always currentProps and nextProps are returning the very same value

currentPropsnextProps 不同(data prop 的引用不同)。您可以通过将 console.log(currentProps.data === nextProps.data) 添加到 areEquals 函数来检查它(它将 return false)。

通过重复使用相同的对象引用,当您对一个对象(例如索引为 0 的对象)进行更改时,它会在 currentProps.data[0]nextProps.data[0] 中更新。您可以通过将 console.log(currentProps.data[0] === nextProps.data[0]) 添加到 areEquals 函数来检查它(它将 return true)。