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
相关产品推荐
相关产品推荐

