React.memo 第二个参数依赖函数没有按预期工作

IT技术 reactjs react-hooks
2021-05-17 15:33:42

我有一个我构建的简单示例,它显示传递给 React.memo 的第二个参数的函数似乎没有像我期望的那样获得以前的属性值。此示例是呈现为按钮的扬声器(对象)的简单列表。按钮上的 onClick 事件导致传递给父组件,父组件中的状态发生变化,并且新的渲染应该只显示更新的组件。

但是,无论先前的状态如何,先前传入的始终与新状态相同。

该示例位于此 URL:https : //stackblitz.com/edit/react-upe9vu

但是,Hello.js在当前导入的组件./Speaker中,如果您将其更改./SpeakerWithMemo为如上行所示,则单击按钮无法更新“收藏夹”状态。

我希望SpeakerWithMemo.js第 12 行的 console.log 会显示 prevProps.speaker.favorite 与 nextProps.speaker.favorite 不同。

相关代码如下:

你好.js

import React, { useState } from "react";

// NO UPDATE HAPPENS ON BUTTON CLICK.
//import Speaker from "./SpeakerWithMemo";

// UPDATE HAPPENS AS EXPECTED ON BUTTON CLICK
import Speaker from "./Speaker";

export default () => {
  const speakersArray = [
    { name: "Crockford", id: 101, favorite: true },
    { name: "Gupta", id: 102, favorite: false },
    { name: "Ailes", id: 103, favorite: true },
  ];

  const [speakers, setSpeakers] = useState(speakersArray);

  const clickFunction = (speakerIdClicked) => {
    var speakersArrayUpdated = speakers.map((rec) => {
      if (rec.id === speakerIdClicked) {
        rec.favorite = !rec.favorite;
      }
      return rec;
    });
    setSpeakers(speakersArrayUpdated);
  };

  return (
    <div>
      {speakers.map((rec) => {
        return (
          <Speaker
            speaker={rec}
            key={rec.id}
            clickFunction={() => {
              clickFunction(rec.id);
            }}
          ></Speaker>
        );
      })}
    </div>
  );
};

SpeakerWithMemo.js

import React from "react";

export default React.memo(
  ({ speaker, clickFunction }) => {
    console.log(`Rendering Speaker ${speaker.name} ${speaker.favorite}`);
    return (
      <button onClick={clickFunction}>
        With Memo {speaker.name} {speaker.id}{" "}
        {speaker.favorite === true ? "true" : "false"}
      </button>
    );
  },
  (prevProps, nextProps) => {
    console.log(
      `memo: ${prevProps.speaker.favorite === nextProps.speaker.favorite} ${
        prevProps.speaker.name
      } fav: prev: ${prevProps.speaker.favorite} next: ${
        nextProps.speaker.favorite
      } `
    );
    return prevProps.speaker.favorite === nextProps.speaker.favorite;
  }
);
1个回答

备忘录没有按预期工作,因为您有一个状态突变。通过不返回新的对象引用,react 在重新渲染时会保释,即props.speaker对象引用永远不会改变,所以 react 假设它与更深入地检查无关。

const clickFunction = (speakerIdClicked) => {
  var speakersArrayUpdated = speakers.map((rec) => {
    if (rec.id === speakerIdClicked) {
      rec.favorite = !rec.favorite; // <-- Mutates the object you try to memoize
    }
    return rec;
  });
  setSpeakers(speakersArrayUpdated);
};

对于您尝试更新的元素,您需要返回一个新的对象引用。此外,当像这样排队切换状态更新时,您应该使用功能状态更新。

const clickFunction = speakerIdClicked => {
  setSpeakers(speakers =>
    speakers.map(rec =>
      rec.id === speakerIdClicked
        ? {
            ...rec, // <-- shallow copy existing value
            favorite: !rec.favorite // <-- update field/property
          }
        : rec
    )
  );
};

https://react-eyvskj.stackblitz.io