Я пытаюсь разобраться 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)
Я не уверен, что useMemo был разработан для запоминания целых компонентов. Это сильно отличается от React.memo. stackoverflow.com/questions/55466104/…
@NicholasTower Я обновил свой вопрос
@nubinub то же самое происходит, когда я использую React.memo с тем же сравнением





Проблема в том, что мемоизированный компонент содержит ссылку на старую версию 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 постоянно меняется, в итоге вы ничего не получите от мемоизации. Это можно было бы улучшить, если бы сам onPress был запомнен с помощью useCallback:
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])
Отлично, не могли бы вы просто показать пример с использованием useCallback? Не уверен, как это сделать
@Orelus Я добавил это в. UseCallback очень похож на useMemo., Он просто предполагает, что вы хотите запомнить функцию. useCallback(() => 'hello world') идентично useMemo(() => () => 'hello world');
что делает onPressCard?