[英]How to avoid re-rendering a functional component when a certain prop changes?
I have a PlayArea component with a number of Card components as children, for a card game.我有一个 PlayArea 组件,其中有许多 Card 组件作为子级,用于纸牌游戏。
The position of the cards is managed by the PlayArea, which has a state value called cardsInPlay
, which is an array of CardData
objects including positional coordinates among other things.卡片的位置由 PlayArea 管理,它有一个称为
cardsInPlay
的状态值,它是一个CardData
对象数组,包括位置坐标等。 PlayArea passes cardsInPlay
and setCardsInPlay
(from useState
) into each Card child component. PlayArea 将
cardsInPlay
和setCardsInPlay
(来自useState
)传递给每个 Card 子组件。
Cards are draggable, and while being dragged they call setCardsInPlay
to update their own position.卡片是可拖动的,在拖动时它们会调用
setCardsInPlay
来更新它们自己的位置。
The result, of course, is that cardsInPlay
changes and therefore every card re-renders.当然,结果是
cardsInPlay
发生了变化,因此每张卡片都会重新呈现。 This may grow costly if a hundred cards make it out onto the table.如果一百张牌出现在桌面上,这可能会增加成本。
How can I avoid this?我怎样才能避免这种情况? Both PlayArea and Card are functional components.
PlayArea 和 Card 都是功能组件。
Here's a simple code representation of that description:这是该描述的简单代码表示:
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} />);
});
It depends on how you pass cardsInPlay
to each Card
component.这取决于您如何将
cardsInPlay
传递给每个Card
组件。 It doesn't matter if the array in state changes as long as you pass only the required information to child.只要您只将所需的信息传递给 child,状态中的数组是否发生变化都没有关系。
Eg:例如:
<Card positionX={cardsInPlay[card.id].x} positionY={cardsInPlay[card.id].y} />
will not cause a re-render, because even i the parent array changes, the instance itself is not getting a new prop.不会导致重新渲染,因为即使父数组发生变化,实例本身也不会获得新的道具。 But if you pass the whole data to each component :
但是,如果您将整个数据传递给每个组件:
<Card cardsInPlay={cardsInPlay} />
it will cause all to re-render because each Card
would get a new prop for every render as no two arrays,objects are equal in Javascript.这将导致所有重新渲染,因为每张
Card
都会为每次渲染获得一个新道具,因为没有两个数组,对象在 Javascript 中是相等的。
PS : Edited after seeing sample code PS:看到示例代码后编辑
The problem is you're passing the entire cardsInPlay
array to each Card
, so React.memo()
will still re-render each card because the props
have changed.问题是你将整个
cardsInPlay
数组传递给每个Card
,所以React.memo()
仍然会重新渲染每张卡片,因为props
已经改变了。 Only pass the element that each card needs to know about and it will only re-render the card that has changed.只传递每张卡片需要知道的元素,它只会重新渲染已更改的卡片。 You can access the previous
cardsInPlay
using the functional update signature of setCardsInPlay()
:您可以使用
setCardsInPlay()
的功能更新签名访问以前的cardsInPlay
:
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} />);
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.