[英]Functional components disappearing with useMemo / React.memo
我試圖繞過useMemo
(或React.memo
)以優化一些組件渲染。
我有一個我無法解釋的問題。
我有以下代碼:
[...]
const [ cards, setCards ] = useState<Card[]>([])
function addCard(){
setCards([...cards, {name: 'card-' + cards.length, counter: 0, type: 'memo'}])
}
function onPressCard(index: number){
cards[index].counter += 1
setCards([...cards])
}
return (
[...]
{
cards.map((x, index) =>
<Card key={index} {...x} onPress={() => onPressCard(index)}/>
}
[...]
)
卡定義為
const Card: FC<CardProps> = function({ name, counter, onPress }){
const counterRef = useRef(0)
const item = useMemo(() => {
counterRef.current +=1
return (
<RectButton onPress={onPress} style={[styles.card, { backgroundColor: 'lightcoral' }]}>
<Text style={styles.text}>{ name }</Text>
<Text style={styles.counter}> { `counter ${counter}` }</Text>
<Text style={styles.counter}>{ `render: ${counterRef.current}`}</Text>
</RectButton>
)
}, [name, counter])
return item
}
為什么當我按列表中的一項(最后一項除外)時,以下所有項都消失了?
編輯: 卡片定義為
const areEqual = function(prevProps: Card, nextProps: Card){
return (
(prevProps.name === nextProps.name) &&
(prevProps.counter === nextProps.counter)
)
}
const Card = React.memo<CardProps>(({ name, counter, onPress }) => {
const counterRef = useRef(0)
counterRef.current +=1
return (
<RectButton onPress={onPress} style={[styles.card, { backgroundColor: 'lightcoral' }]}>
<Text style={styles.text}>{ name }</Text>
<Text style={styles.counter}> { `counter ${counter}` }</Text>
<Text style={styles.counter}>{ `render: ${counterRef.current}`}</Text>
</RectButton>
)
}, areEqual)
問題在於,記憶化組件包含對onPress
的舊版本的引用。 那個舊的onPress
封閉了一個舊版本的cards
。 因此,點擊該按鈕將調用舊功能,該功能會根據該舊狀態更新父級的狀態,並且該舊狀態中包含的項較少。
解決此問題的一種方法是使用setCards的功能版本,以便使更新基於最新狀態。 另外,我更新了代碼以不再更改舊卡:
function onPressCard(index: number){
setCards(oldCards => {
const newCards = [...oldCards];
newCards[index] = {...oldCards[index]};
newCards[index].counter += 1;
return newCards;
})
}
另一個選擇是將onPress添加到useMemo的條件中,但是由於onPress函數一直在變化,因此最終不會真正從備忘錄中獲得任何收益。 如果使用useCallback記住onPress本身,則可以改進此方法:
const onPressCard = useCallback((index: number) => {
cards[index].counter += 1;
setCards([...cards]);
}, [cards])
// ...
const item = useMemo(() => {
counterRef.current +=1
return ( /* jsx omitted for brevity */ )
}, [name, counter, onPress])
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.