React Native报错:渲染CellRenderer时无法更新Categoriess组件求解决
解决React警告:Cannot update a component while rendering a different component
问题根源
警告的核心原因是你在FlatList的renderItem函数中直接调用了setHomeId(item.id)——这属于在组件渲染阶段更新父组件(Categoriess)的状态,违反了React的渲染规则:渲染过程应该是纯函数,不能产生副作用(比如状态更新)。每次setHomeId触发重渲染,又会再次执行renderItem,陷入循环触发警告。
修复步骤
提前计算
homeID,避免渲染时更新状态
在获取分类数据后,直接在useEffect关联的请求逻辑中筛选出符合level === 1 && afterCategoryId === null的分类ID,一次性设置homeID,而不是在渲染时动态处理。优化数据结构,避免渲染时重复遍历
提前将分类数据整理为父子层级映射,减少renderItem中的重复计算和遍历,提升组件性能。
修改后的完整代码
import { DrawerActions, useNavigation } from '@react-navigation/native'; import { Divider, NativeBaseProvider } from 'native-base'; import fetch from 'node-fetch'; import React, { useEffect, useState, useMemo } from 'react'; import { ActivityIndicator, FlatList, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import AntDesign from 'react-native-vector-icons/AntDesign'; import { API_KEY, URL } from '../../globalVariable'; const Categoriess = () => { const [data, setData] = useState([]); const [homeID, setHomeId] = useState(null); const [loading, setLoading] = useState(true); const navigation = useNavigation(); const option = () => { let url = `${URL}/store-api/category`; let options = { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'sw-access-key': `${API_KEY}`, }, }; fetch(url, options) .then(res => res.json()) .then(json => { const categories = !!json?.elements ? json.elements : []; setData(categories); // 提前筛选符合条件的homeID const homeCategory = categories.find(item => item.level === 1 && item.afterCategoryId === null); setHomeId(homeCategory?.id || null); setLoading(false); }) .catch(err => { setLoading(false); console.error('获取分类失败:', err); }); }; useEffect(() => { option(); }, []); // 预整理子分类映射,避免renderItem中重复遍历全量数据 const childCategoriesMap = useMemo(() => { return data.reduce((map, item) => { if (item.parentId) { map[item.parentId] = [...(map[item.parentId] || []), item]; } return map; }, {}); }, [data]); return ( <View> <TouchableOpacity onPress={() => navigation.dispatch(DrawerActions.closeDrawer())}> <View style={styles.closeDrawer}> <Text style={styles.text}>Close Menu</Text> <AntDesign name='close' size={30} /> </View> </TouchableOpacity> <Text style={{ fontSize: 25, fontWeight: 'bold', textAlign: 'center' }}> Categories </Text> {loading ? ( <ActivityIndicator /> ) : ( <FlatList data={data} initialNumToRender={7} renderItem={({ item }) => { // 只渲染符合条件的分类项 if (item.visible !== true || item.parentId !== homeID) { return null; } const childCategories = childCategoriesMap[item.id] || []; return ( <View> <TouchableOpacity onPress={() => navigation.navigate('Products', item)}> <Text style={styles.category}>{item.name}</Text> <NativeBaseProvider> <Divider thickness={3} bg="red.200" /> </NativeBaseProvider> </TouchableOpacity> {childCategories.map((curElem) => ( <TouchableOpacity key={curElem.id} // 使用唯一ID作为key,避免index导致的渲染异常 onPress={() => navigation.navigate('Products', curElem)}> <Text style={styles.subCategory}> - {curElem.name} </Text> <NativeBaseProvider> <Divider thickness={1} /> </NativeBaseProvider> </TouchableOpacity> ))} </View> ); }} /> )} </View> ); }; export default Categoriess; const styles = StyleSheet.create({ category: { marginHorizontal: 15, marginVertical: 5, paddingVertical: 10, fontSize: 18, color: '#6779b4', fontFamily: "Montserrat-Bold" }, subCategory: { marginLeft: 30, marginBottom: 10, fontSize: 15, paddingVertical: 10, fontFamily: "Montserrat-Bold" }, closeDrawer: { flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 10, paddingVertical: 5, }, text: { fontSize: 18, fontFamily: "Montserrat-Bold" }, });
关键修改说明
- 移除
renderItem中的setHomeId调用,改为在数据请求完成后一次性计算并设置homeID,彻底避免渲染阶段的状态更新。 - 用
useMemo创建childCategoriesMap,提前整理子分类与父分类的映射关系,减少渲染时的重复计算。 - 将嵌套的
data.map改为读取预整理的子分类数组,简化渲染逻辑。 - 替换列表项的
key为分类唯一id,避免使用index作为key可能引发的组件渲染异常。 - 增加请求错误日志,方便排查数据获取失败的问题。
内容的提问来源于stack exchange,提问作者user20075027
相关产品推荐
相关产品推荐

