当某个道具更改时,如何避免重新渲染功能组件?

时间:2020-09-27 19:39:53

标签: javascript reactjs react-props use-state react-state

我有一个PlayArea组件,其中有许多作为孩子的Card组件,用于打牌游戏。

纸牌的位置由PlayArea管理,PlayArea的状态值为cardsInPlay,它是CardData对象的数组,其中包括位置坐标。 PlayArea将cardsInPlaysetCardsInPlay(来自useState)传递到每个Card子元素。

卡是可拖动的,在被拖动时,它们会调用setCardsInPlay来更新自己的位置。

当然,结果是cardsInPlay发生了变化,因此每张纸牌都会重新出现。如果将一百张纸牌放在桌子上,这可能会增加成本。

>

如何避免这种情况? PlayArea和Card都是功能组件。

以下是该描述的简单代码表示形式:

const PlayArea = () => {
  const [cardsInPlay, setCardsInPlay] = useState([]);

  return (
    <>
      { cardsInPlay.map(card => (
        <Card
          key={card.id}
          card={card}
          cardsInPlay={cardsInPlay}
          setCardsInPlay={setCardsInPlay} />
      }
    </>
  );
}

const Card = React.memo({card, cardsInPlay, setCardsInPlay}) => {
    const onDrag = (moveEvent) => {
       setCardsInPlay(
         cardsInPlay.map(cardInPlay => {
           if (cardInPlay.id === card.id) {
              return {
               ...cardInPlay,
               x: moveEvent.clientX,
               y: moveEvent.clientY
              };
           }
           return cardInPlay;
        }));
      };

      return (<div onDrag={onDrag} />);
   });

          

2 个答案:

答案 0 :(得分:2)

这取决于您如何将cardsInPlay传递给每个Card组件。只要您仅将所需的信息传递给child,状态数组就不会改变。

例如:

<Card positionX={cardsInPlay[card.id].x} positionY={cardsInPlay[card.id].y} /> 

不会导致重新渲染,因为即使父数组更改了,实例本身也不会获得新的支持。但是,如果您将整个数据传递给每个组件:

<Card cardsInPlay={cardsInPlay} /> 

这将导致所有人重新渲染,因为每个Card每次渲染都会获得一个新的道具,因为没有两个数组,Javascript中的对象相等。

P.S:看到示例代码后进行编辑

答案 1 :(得分:1)

问题是您要将整个cardsInPlay数组传递给每个Card,因此React.memo()仍将重新渲染每张卡片,因为props 具有已更改。仅传递每张卡需要了解的元素,它只会重新呈现已更改的卡。您可以使用cardsInPlay的{​​{3}}签名访问上一个setCardsInPlay()

const PlayArea = () => {
  const [cardsInPlay, setCardsInPlay] = useState([]);
  const cards = cardsInPlay.map(
    card => (
      <Card
        key={card.id}
        card={card}
        setCardsInPlay={setCardsInPlay} />
    )
  );

  return (<>{cards}</>);
};

const Card = React.memo(({ card, setCardsInPlay }) => {
  const onDrag = (moveEvent) => {
    setCardsInPlay(
      cardsInPlay => cardsInPlay.map(cardInPlay => {
        if (cardInPlay.id === card.id) {
          return {
            ...cardInPlay,
            x: moveEvent.clientX,
            y: moveEvent.clientY
          };
        }
        return cardInPlay;
      })
    );
  };

  return (<div onDrag={onDrag} />);
});