【问题标题】:How to prevent a whole list from re-rendering when editing an element on the list?编辑列表中的元素时如何防止整个列表重新呈现?
【发布时间】:2023-03-11 16:28:01
【问题描述】:

我创建了这个非常简单的应用程序来解释这个问题。

我尝试使用记忆和回调,但我相信它会重新渲染,因为一旦我输入文本输入,playerArr 总是会发生变化。

我的实际列表大小只有 15 个元素,但重新渲染导致它在输入输入时变得非常慢。

有什么建议吗?我有一个最后期限,我感到压力很大 =( 会回到非钩子帮助吗?或实施 redux?不确定性能因素。

function App() {
  const [player1, setPlayer1] = React.useState({
    firstName: "First",
    lastName: "Last ",
    id: uuidv4()
  });

  const [player2, setPlayer2] = React.useState({
    firstName: "First",
    lastName: "Last",
    id: uuidv4()
  });

  const [player3, setPlayer3] = React.useState({
    firstName: "First",
    lastName: "Last",
    id: uuidv4()
  });

  return (
    <div>
      <State
        player1={player1}
        player2={player2}
        player3={player3}
        setPlayer1={setPlayer1}
        setPlayer2={setPlayer2}
        setPlayer3={setPlayer3}
      />
    </div>
  );
}

//----------------------------------------------------------

export const State = React.memo(({player1, player2, player3, setPlayer1, setPlayer2, setPlayer3}) => {
  const playerArr = [player1, player2, player3];
  const setPlayerArr = [setPlayer1, setPlayer2, setPlayer3];

  return (
    <div>
      <Playlist
        playerArr={playerArr}
        setPlayerArr={setPlayerArr}
      />
    </div>
  );
});

//----------------------------------------------------------

export const Playlist = React.memo(({playerArr, setPlayerArr}) => {
  return (
    <div>
      {
        playerArr.map((player, index) => (
          <Player
            key={player.id}
            player={player}
            setPlayer={setPlayerArr[index]}
          />
        ))
      }
    </div>
  );
});

//----------------------------------------------------------

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

  return (
    <div>
      <input type={"text"} name={"firstName"} value={player.firstName} onChange={handleOnChange}/>
      <input type={"text"} name={"lastName"} value={player.lastName} onChange={handleOnChange}/>
    </div>
  );
});

编辑:我根据讨论编辑了应用程序。同样的事情发生

【问题讨论】:

  • 那是因为你定义了渲染发生的播放器数组。
  • @JohnRuddell 嘿嘿!那么解决这个问题的最佳方法是什么?谢谢你昨晚的帮助
  • 哦嘿哈哈!我可能会将此作为类组件大声笑
  • @JohnRuddell 哈哈。将它作为一个类组件真的会阻止重新渲染吗?为什么这与钩子相比会有所不同?我基本上还是会做同样的事情,不是吗?
  • 我可能会做这样的事情。 codesandbox.io/s/react-example-63f0t 。如果您查看控制台日志,您会看到正在呈现的项目。除非您确实需要,否则您不应该真正担心性能方面的任何较重的事情。

标签: javascript reactjs


【解决方案1】:

无论您做什么,您的 &lt;App&gt;&lt;Playlist&gt; 组件(即使它们已被记忆)在每次有用户输入时都必须重新渲染,因为这是您存储状态的地方,并且是意料之中。

您可以做的最好的事情是记住每个&lt;Player&gt; 组件,这样当列表重新呈现时,每个单独的列表项不一定会重新呈现自己。为此,您可以将“areEqual”函数作为第二个参数传递给React.memo。请参阅 React 文档中的示例:https://reactjs.org/docs/react-api.html#reactmemo

在你的情况下,它可能看起来像这样:

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

  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.firstName === nextProps.firstName
    && prevProps.lastName === nextProps.lastName
    && prevProps.id === nextProps.id) {
    return true; // Return true if they ARE the same
  } else {
    return false; // Return false if they are NOT the same
  }
});

有时,如果您要比较的数据是字符串和/或数字的简单集合,您可以使用JSON.stringify 作为速记方式将其转换为字符串并比较字符串:

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

  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 (JSON.stringify(prevProps) === JSON.stringify(nextProps)) {
    return true; // Return true if they ARE the same
  } else {
    return false; // Return false if they are NOT the same
  }
});

【讨论】:

  • 嘿,你的理论绝对有道理。我认为没有真正的解决方法,但我喜欢你的记忆示例和理论。虽然我确实尝试将它应用于我当前的代码github.com/DaleSalcedo/TestingListRendering,但它不再让我输入文本框了。我不知道为什么
  • 实际上我在比较道具的 return true 和 return false 中设置了一个断点,似乎每次我输入它都会命中 return true 所有 3 次,而不是其中 1 个变为 false。
  • 好吧,没关系,看来我需要做 prevProps.player.firstName 而不是 prevProps.firstName。让我修一下,然后告诉你
  • 好的,我让它工作了。不幸的是,使用 React 插件 --> 选项 --> 突出显示更新似乎没有什么不同。但是你所说的逻辑上最有意义,我很确定这是它会得到的最好的。将把这个留给更多的讨论,看看其他人的想法和建议。谢谢你。我将把它应用到我的实际工作应用程序中,看看这是否有帮助,并会让你知道
  • 嘿,我正在尝试将您的示例用于稍微复杂一点的东西,但我无法让它工作,因为它将文本输入返回到旧值?这很奇怪.. 但我在这里做了一个演示.. 没有人回答希望你能看看.. 我很绝望:stackoverflow.com/questions/56944519/…
猜你喜欢
  • 2022-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-17
  • 1970-01-01
  • 2021-12-05
  • 2020-02-21
  • 2012-06-27
相关资源
最近更新 更多