简体   繁体   English

比较 React.memo 组件中的 prevProps 和 nextProps 以防止不必要的重新渲染,但这很有趣

[英]Comparing prevProps and nextProps in a React.memo Component to prevent unnecessary re-rendering but it's acting funny

I made a demo here: https://codesandbox.io/s/interesting-germain-mgd64我在这里做了一个演示: https : //codesandbox.io/s/interesting-germain-mgd64

If you type something in one person's name, then switch to another person and type around, you will see it will change the other person's name back to the original.如果你用一个人的名字输入一些东西,然后切换到另一个人并输入,你会看到它会将另一个人的名字改回原来的名字。 But it doesn't make sense why it's doing that.但它为什么这样做是没有意义的。 If I remove the whole prevProps and nextProps comparison it works fine.如果我删除整个 prevProps 和 nextProps 比较它工作正常。

Though this works in a much more simpler example just fine.虽然这在一个更简单的例子中工作得很好。 I'm not sure why it's not working in this scenario though.我不确定为什么它在这种情况下不起作用。

More detail on what I'm trying to do as far as comparing props found here: https://reactjs.org/docs/react-api.html#reactmemo就比较这里找到的道具而言,我正在尝试做的更多细节: https : //reactjs.org/docs/react-api.html#reactmemo

Player Component using the Memo is here:使用备忘录的播放器组件在这里:

export const Player = React.memo(({player, modifyPlayer}) => {
  const handleOnChange = React.useCallback((event) => {
    modifyPlayer(player.id, event.target.name, event.target.value);
  }, [player, modifyPlayer]);

  return (
    <div>
      <input type={"text"} name={"firstName"} value={player.firstName} onChange={handleOnChange}/>
      <input type={"text"} name={"lastName"} value={player.lastName} onChange={handleOnChange}/>
    </div>
  );
}, 
(prevProps, nextProps) => {
  // Check to see if the data is the same
  if (prevProps.player.firstName === nextProps.player.firstName
    && prevProps.player.lastName === nextProps.player.lastName
    && prevProps.player.id === nextProps.player.id) {
    return true; // Return true if they ARE the same
  } else {
    return false; // Return false if they are NOT the same
    // EVEN THOUGH THIS RETURNS FALSE IT MESSES UP THE OTHER TEXT
  }
});

App Component应用组件

function App() {
  const [playerDict, setPlayerDict] = React.useState(
    {
      id1: {
        firstName: "John",
        lastName: "Doe",
        id: 'id1'
      },
      id2: {
        firstName: "Michael",
        lastName: "Creaton",
        id: 'id2'
      },
      id3: {
        firstName: "William",
        lastName: "Shakespeare",
        id: 'id3'
      },
    }

  );
  const [playerIdList, setPlayerIdList] = React.useState(['id1', 'id2', 'id3']);

  const modifyPlayer = React.useCallback((playerId, propertyName, value) => {
    const playerCopy = {...playerDict[playerId]};
    playerCopy[propertyName] = value;
    const playerDictCopy = {
      ...playerDict,
      [playerId]: playerCopy
    };
    setPlayerDict(playerDictCopy);
  }
  ,[playerDict]);

  return (
    <div>
      <Playlist
        modifyPlayer={modifyPlayer}
        playlist={playerIdList.map(playerId => playerDict[playerId])}
      />
    </div>
  );
}

Playlist Component播放列表组件

export const Playlist = React.memo(({modifyPlayer, playlist}) => {
  return (
    <div>
      {
        playlist.map((player) => (
          <Player
            key={player.id}
            player={player}
            modifyPlayer={modifyPlayer}
          />
        ))
      }
    </div>
  );
});

That was a good head-scratcher, but I think I found the issue.这是一个很好的头疼,但我想我发现了问题。

When you create your callback modifyPlayer you rightly pass playerDict as a dependency, since the callback relies on having an "up to date" version of playerDict before it can create a new version of playerDict using the latest update event.当您创建回调modifyPlayer您正确地将playerDict作为依赖项传递,因为回调依赖于拥有“最新”版本的playerDict然后才能使用最新的更新事件创建新版本的playerDict But this also means that each time the the depencendy playerDict changes (with every change event) you will get a new modifyPlayer -callback function.但这也意味着每次playerDict改变(每次改变事件)你都会得到一个新的modifyPlayer函数。

But the areEqual function in the Player -component (around line 29) says that the component should only update if any of the player properties have changed (id, firstName or lastName), this means that the component never receives the updated callback-function, so it will try to update the the playerDict using the "wrong" previous version of the data (because it has a previous version of the callback function).但是Player组件中的areEqual函数(第 29 行左右)表示该组件仅应在任何player属性发生更改(id、firstName 或 lastName)时更新,这意味着该组件永远不会收到更新的回调函数,所以它会尝试使用“错误的”先前版本的数据更新playerDict (因为它具有先前版本的回调函数)。

Including a check for equality between prevProps.modifyPlayer and nextProps.modifyPlayer makes the code behave like expected again:包括检查prevProps.modifyPlayernextProps.modifyPlayer之间的prevProps.modifyPlayer nextProps.modifyPlayer使代码再次像预期的那样运行:

// in Player.js - around line 29
(prevProps, nextProps) => {
    // Check to see if the data is the same
    if (
      prevProps.player.firstName === nextProps.player.firstName &&
      prevProps.player.lastName === nextProps.player.lastName &&
      prevProps.player.id === nextProps.player.id && 
      prevProps.modifyPlayer === nextProps.modifyPlayer
    ) {
      return true; // Return true if they ARE the same
    } else {
      return false; // Return false if they are NOT the same
    }
  }

Edit: Updated demo code here https://codesandbox.io/s/damp-surf-nu6tt编辑:在此处更新演示代码https://codesandbox.io/s/damp-surf-nu6tt

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM