如何避免在某个 prop 发生变化时重新渲染功能组件?
How to avoid re-rendering a functional component when a certain prop changes?
我有一个 PlayArea 组件,其中有许多 Card 组件作为子组件,用于纸牌游戏。
卡片的位置由 PlayArea 管理,它有一个名为 cardsInPlay
的状态值,它是 CardData
对象的数组,包括位置坐标等。 PlayArea 将 cardsInPlay
和 setCardsInPlay
(来自 useState
)传递到每个 Card 子组件中。
卡片是可拖动的,在被拖动时它们会调用 setCardsInPlay
来更新它们自己的位置。
当然,结果是 cardsInPlay
发生变化,因此 每张卡片都会重新渲染。 如果有一百张卡片出现在table.
我怎样才能避免这种情况? 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} />);
});
这取决于您如何将 cardsInPlay
传递给每个 Card
组件。只要你只传递需要的信息给child.
例如:
<Card positionX={cardsInPlay[card.id].x} positionY={cardsInPlay[card.id].y} />
不会导致 re-render,因为即使父数组发生变化,实例本身也不会获得新的 prop。但是,如果您将整个数据传递给每个组件:
<Card cardsInPlay={cardsInPlay} />
它会导致所有 re-render 因为每个 Card
都会为每个渲染获得一个新的道具,因为没有两个数组,对象在 Javascript.
中是相等的
P.S : 看到示例代码后编辑
问题是您将整个 cardsInPlay
数组传递给每个 Card
,因此 React.memo()
仍将 re-render 每张卡片,因为 props
已经改变了。只传递每张卡片需要知道的元素,它只会 re-render 发生变化的卡片。您可以使用 setCardsInPlay()
的 functional update 签名访问之前的 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} />);
});
我有一个 PlayArea 组件,其中有许多 Card 组件作为子组件,用于纸牌游戏。
卡片的位置由 PlayArea 管理,它有一个名为 cardsInPlay
的状态值,它是 CardData
对象的数组,包括位置坐标等。 PlayArea 将 cardsInPlay
和 setCardsInPlay
(来自 useState
)传递到每个 Card 子组件中。
卡片是可拖动的,在被拖动时它们会调用 setCardsInPlay
来更新它们自己的位置。
当然,结果是 cardsInPlay
发生变化,因此 每张卡片都会重新渲染。 如果有一百张卡片出现在table.
我怎样才能避免这种情况? 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} />);
});
这取决于您如何将 cardsInPlay
传递给每个 Card
组件。只要你只传递需要的信息给child.
例如:
<Card positionX={cardsInPlay[card.id].x} positionY={cardsInPlay[card.id].y} />
不会导致 re-render,因为即使父数组发生变化,实例本身也不会获得新的 prop。但是,如果您将整个数据传递给每个组件:
<Card cardsInPlay={cardsInPlay} />
它会导致所有 re-render 因为每个 Card
都会为每个渲染获得一个新的道具,因为没有两个数组,对象在 Javascript.
P.S : 看到示例代码后编辑
问题是您将整个 cardsInPlay
数组传递给每个 Card
,因此 React.memo()
仍将 re-render 每张卡片,因为 props
已经改变了。只传递每张卡片需要知道的元素,它只会 re-render 发生变化的卡片。您可以使用 setCardsInPlay()
的 functional update 签名访问之前的 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} />);
});