【问题标题】:React.memo 2nd Parameter Dependency Function Not Working as ExpectedReact.memo 第二个参数依赖函数没有按预期工作
【发布时间】:2020-11-17 06:53:42
【问题描述】:

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

但是,不管之前的状态如何,传入的previous总是和新的状态一样。

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

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

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

相关代码如下:

Hello.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;
  }
);

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案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);
    };
    

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

    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

    【讨论】:

    • 感谢@DrewReese(再次)。就我而言,总的新秀动作。我知道比改变状态更好。回到我做 redux 的时候,有一些中间件在我做的时候抛出了一个错误。现在有什么可以帮助在开发时避免此类问题的吗?
    • @Pete 我认为有一些不可变库(我听说过 immutablejs),但我认为这主要是良好的编码纪律和对细节的关注。 IDK,一段时间后,您就会对常见错误可能会偷偷摸摸的地方有所了解。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    相关资源
    最近更新 更多