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

时间:2019-07-12 15:30:07

标签: 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)}/>
    }
    [...]
)

定义为


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
}

为什么当我按列表中的一项(最后一项除外)时,以下所有项都消失了? enter image description here

编辑:定义为

卡片也会发生同样的情况
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)

1 个答案:

答案 0 :(得分:5)

问题在于,记忆成分包含对旧版本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])