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

IT技术 javascript reactjs
2021-05-14 01:39:11

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

如果你用一个人的名字输入一些东西,然后切换到另一个人并输入,你会看到它会将另一个人的名字改回原来的名字。但它为什么这样做是没有意义的。如果我删除整个 prevProps 和 nextProps 比较它工作正常。

虽然这在一个更简单的例子中工作得很好。我不确定为什么它在这种情况下不起作用。

就比较这里找到的props而言,我正在尝试做的更多细节:https : //reactjs.org/docs/react-api.html#reactmemo

使用备忘录的播放器组件在这里:

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

应用组件

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

播放列表组件

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

这是一个很好的头疼,但我想我发现了问题。

当您创建回调时,modifyPlayer您正确地将其playerDict作为依赖项传递,因为回调依赖于具有“最新”版本,playerDict 然后才能创建playerDict使用最新更新事件的新版本但这也意味着每次依赖关系playerDict发生变化时(随着每个变化事件),您都会获得一个新的modifyPlayer-callback 函数。

但是-component 中areEqual函数Player(第 29 行左右)表示该组件仅应在任何player属性发生更改(id、firstName 或 lastName)时更新,这意味着该组件永远不会收到更新的回调函数,因此它将尝试playerDict使用“错误的”先前版本的数据更新(因为它具有先前版本的回调函数)。

包括平等的检查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
    }
  }

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