功能组件随着useMemo / React.memo消失

Ore*_*lus 2 reactjs react-native

我正在努力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)}/>
    }
    [...]
)

Run Code Online (Sandbox Code Playgroud)

定义为


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
}
Run Code Online (Sandbox Code Playgroud)

为什么当我按列表中的一项(最后一项除外)时,以下所有项都消失了? 在此处输入图片说明

编辑:卡片定义为

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)
Run Code Online (Sandbox Code Playgroud)

Nic*_*wer 5

问题是备忘的组件包含对的旧版本的引用onPress。该旧onPress版本cards在关闭时具有旧版本。因此,点击该按钮将调用旧功能,该功能会根据该旧状态更新父级的状态,并且该旧状态中包含的项较少。

解决此问题的一种方法是使用setCards的功能版本,以便使更新基于最新状态。另外,我更新了代码以不再更改旧卡:

function onPressCard(index: number){
  setCards(oldCards => {
    const newCards = [...oldCards];
    newCards[index] = {...oldCards[index]};
    newCards[index].counter += 1;
    return newCards;
  })
}
Run Code Online (Sandbox Code Playgroud)

另一个选择是将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])    
Run Code Online (Sandbox Code Playgroud)