You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native:解决数组未更新元素重渲染致ImageComponent闪烁问题

问题:点击数组元素增减按钮时所有ImageComponent组件闪烁

点击数组元素的增减按钮时,所有元素的ImageComponent都会闪烁,推测单个元素更新时数组内所有元素都发生了重渲染,尝试使用memo、useMemo及useCallback优化但未奏效,求排查。

主组件代码

const [addList, setAddList] = useState([
        { name: 'red bull', icon: 'beverage', count: 0 },
        { name: 'beer', icon: 'alcohol', count: 0 }
    ]);

const changeUnitCount = useCallback((index, change) => {
        // console.log('changeUnitCount rendered for', addList[index].name);
        setAddList((prev) => {
            const newCount = prev[index].count + change;
            if (newCount < 0) {
                Alert.alert(`Delete ${prev[index].name}`, 'Are you sure you want to delete this?', [
                    {
                        text: 'Cancel',
                        style: 'cancel'
                    },
                    {
                        text: 'Delete',
                        onPress: () => {
                            setAddList(prev => [...prev.slice(0, index), ...prev.slice(index + 1)]);
                        },
                        style: 'destructive'
                    },
                ]);
            } else {
                const updatedList = [...prev];
                updatedList[index] = { ...updatedList[index], count: newCount };
                return updatedList;
            }
            return prev;
        });
    }, []);

const Unit = React.memo(({ drink, index, changeUnitCount }) => {
        useEffect(() => {
            console.log(`${drink.name} component rerendered: ${drink.count}`);
        });
        return (
            <View style={styles.addUnitContainer}>
                <TouchableOpacity onPress={() => changeUnitCount(index, -1)}>
                    <Icon name="remove" color="#c1dfb0" size={50} />
                </TouchableOpacity>
                <View style={styles.addUnit}>
                    <ImageComponent type={'drink'} value={drink.icon} size={30} />
                    <Text style={styles.text}>{drink.name}</Text>
                </View>
                <Text style={styles.text}>{drink.count}</Text>
                <TouchableOpacity onPress={() => changeUnitCount(index, 1)}>
                    <Icon name="add" color="#c1dfb0" size={50} />
                </TouchableOpacity>
            </View>
        );
    });

return (
    ...
    {addList.map((item, i) => (
                    <Unit key={`${item.name}`} index={i} drink={item} changeUnitCount={changeUnitCount} />
                ))}
);

ImageComponent.jsx代码

const ImageComponent = memo(({ type, value, size = 30 }) => {
  const source = getImage(type, value);

  if (!source) {
    return null; // Return null if the source is invalid
  }

  console.log(`Image rendered: ${type} ${value}`);

  return (
    <Image
      style={{ width: size, height: size }}
      source={source}
      contentFit="contain"
    />
  );
}, (prevProps, nextProps) => prevProps.value === nextProps.value && prevProps.size === nextProps.size);

export default ImageComponent;

解决方案

1. 核心问题:将Unit组件移到主组件外部

你把Unit组件定义在主组件的函数内部了,每次主组件重渲染时,Unit都会被重新定义为一个全新的组件类型。React会认为这是完全不同的组件,会卸载旧组件并重新挂载新组件,导致所有Unit都强制重渲染,React.memo完全失效。

修改方式:把Unit组件移到主组件的外部(比如单独的文件,或者同一文件的主组件函数外面):

// 移到主组件外部
const Unit = React.memo(({ drink, index, changeUnitCount }) => {
    useEffect(() => {
        console.log(`${drink.name} component rerendered: ${drink.count}`);
    }, [drink.name, drink.count]); // 加上依赖数组,避免不必要的日志
    return (
        <View style={styles.addUnitContainer}>
            <TouchableOpacity onPress={() => changeUnitCount(index, -1)}>
                <Icon name="remove" color="#c1dfb0" size={50} />
            </TouchableOpacity>
            <View style={styles.addUnit}>
                <ImageComponent type={'drink'} value={drink.icon} size={30} />
                <Text style={styles.text}>{drink.name}</Text>
            </View>
            <Text style={styles.text}>{drink.count}</Text>
            <TouchableOpacity onPress={() => changeUnitCount(index, 1)}>
                <Icon name="add" color="#c1dfb0" size={50} />
            </TouchableOpacity>
        </View>
    );
});

// 主组件
const MainComponent = () => {
    // ... 原有的state和逻辑
    return (
        // ... 原有的渲染内容
        {addList.map((item, i) => (
            <Unit key={`${item.name}`} index={i} drink={item} changeUnitCount={changeUnitCount} />
        ))}
    );
};

2. 完善ImageComponent的memo比较逻辑

当前ImageComponent的memo比较函数没有校验type参数,虽然你现在传的是固定的'drink',但为了严谨性,建议加上type的比较,避免未来参数变化时出现意外重渲染:

const ImageComponent = memo(({ type, value, size = 30 }) => {
  // ... 原有的逻辑
}, (prevProps, nextProps) => 
  prevProps.type === nextProps.type && 
  prevProps.value === nextProps.value && 
  prevProps.size === nextProps.size
);

3. 优化useEffect的依赖

给Unit里的useEffect加上明确的依赖数组,避免每次组件渲染都执行日志输出,干扰你判断组件是否真的重渲染:

useEffect(() => {
    console.log(`${drink.name} component rerendered: ${drink.count}`);
}, [drink.name, drink.count]); // 只有当name或count变化时才执行

完成以上修改后,点击增减按钮时,只有被修改的那个Unit组件会重渲染,对应的ImageComponent也只会在自身props变化时才更新,不会出现所有组件闪烁的问题。

内容的提问来源于stack exchange,提问作者seans

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 06:57:06