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

React Native报错:渲染CellRenderer时无法更新Categoriess组件求解决

解决React警告:Cannot update a component while rendering a different component

问题根源
警告的核心原因是你在FlatList的renderItem函数中直接调用了setHomeId(item.id)——这属于在组件渲染阶段更新父组件(Categoriess)的状态,违反了React的渲染规则:渲染过程应该是纯函数,不能产生副作用(比如状态更新)。每次setHomeId触发重渲染,又会再次执行renderItem,陷入循环触发警告。

修复步骤

  1. 提前计算homeID,避免渲染时更新状态
    在获取分类数据后,直接在useEffect关联的请求逻辑中筛选出符合level === 1 && afterCategoryId === null的分类ID,一次性设置homeID,而不是在渲染时动态处理。

  2. 优化数据结构,避免渲染时重复遍历
    提前将分类数据整理为父子层级映射,减少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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 17:01:07