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

React Native底部导航图标渲染报错:Element type is invalid

底部导航图标渲染错误排查与解决

错误提示

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: number.
Check the render method of Icon.

此前代码在React Native 0.66.4、API 29搭配OpenJDK8环境下可正常运行,回退版本后仍出现该渲染问题。

相关代码文件

TabItem.js

import React from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'
import { IconAbout, IconAboutActive, IconHome, IconHomeActive, IconNearby, IconNearbyActive} from '../../assets'
import { WARNA_UTAMA, WARNA_DISABLE } from '../../utils/constant'

const TabItem = ({ isFocused, onPress, onLongPress, label }) => {
    const Icon = () => {
        if (label === 'Home') return isFocused ? <IconHomeActive /> : <IconHome />
        if (label === 'Nearby') return isFocused ? <IconNearbyActive /> : <IconNearby />
        if (label === 'About') return isFocused ? <IconAboutActive /> : <IconAbout />
      
        return <IconHome />
      }
    return (
        <TouchableOpacity
            onPress={onPress}
            onLongPress={onLongPress}
            style={styles.container}>
            <Icon />
            <Text style={styles.text(isFocused)}>
              {label}
            </Text>
          </TouchableOpacity>
    )
}

export default TabItem

const styles = StyleSheet.create({
    container:{
        alignItems: 'center',
    },
    text: (isFocused) =>  ({
        fontSize: 13,
        color: isFocused ? WARNA_UTAMA : WARNA_DISABLE, 
    })
})

BottomNavigator.js

import React from 'react'
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native'
import TabItem from '../TabItem';

const BottomNavigator = ({ state, descriptors, navigation }) => {
    const focusedOptions = descriptors[state.routes[state.index].key].options;

  if (focusedOptions.tabBarVisible === false) {
    return null;
  }

  return (
    <View style={ styles.container }>
      {state.routes.map((route, index) => {
        const { options } = descriptors[route.key];
        const label =
          options.tabBarLabel !== undefined
            ? options.tabBarLabel
            : options.title !== undefined
            ? options.title
            : route.name;

        const isFocused = state.index === index;

        const onPress = () => {
          const event = navigation.emit({
            type: 'tabPress',
            target: route.key,
            canPreventDefault: true,
          });

          if (!isFocused && !event.defaultPrevented) {
            navigation.navigate(route.name);
          }
        };

        const onLongPress = () => {
          navigation.emit({
            type: 'tabLongPress',
            target: route.key,
          });
        };

        return (
          <TabItem 
              key={index}
              label={label}
              isFocused={isFocused}
              onPress={onPress}
              onLongPress={onLongPress}
          />
        );
      })}
    </View>
  );
}

export default BottomNavigator

const styles = StyleSheet.create({
    container:{
        flexDirection: 'row',
        backgroundColor: '#FFFF',
        justifyContent: 'space-between',
        paddingHorizontal: 48,
        paddingVertical: 8,
        borderRadius: 50,
    }
})

Route.js

import React from 'react';
import {StyleSheet} from 'react-native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {About, Home, Nearby, Splash} from '../pages';
import {BottomNavigator} from '../components';

const Stack = createNativeStackNavigator();
const Tab = createBottomTabNavigator();

const MainApp = () => {
  return (
      {
        <Tab.Navigator tabBar={props => <BottomNavigator {...props} />}>
          <Tab.Screen
            name="Home"
            component={Home}
            options={{headerShown: false}}
          />
          <Tab.Screen
            name="Nearby"
            component={Nearby}
            options={{headerShown: false}}
          />
          <Tab.Screen
            name="About"
            component={About}
            options={{headerShown: false}}
          />
        </Tab.Navigator>
      }
  );
};

const Router = () => {
  return (
    <Stack.Navigator initialRouteName="Splash">
      <Stack.Screen
        name="Splash"
        component={Splash}
        options={{headerShown: false}}
      />
      <Stack.Screen
        name="MainApp"
        component={MainApp}
        options={{headerShown: false}}
      />
    </Stack.Navigator>
  );
};

export default Router;

const styles = StyleSheet.create({});

错误原因

核心问题是你导入的IconHome、IconHomeActive等并非React组件,而是数字类型的本地资源ID(比如通过require('./icon.png')导入图片时,React Native会返回一个数字ID)。但代码中直接以<IconHome />的方式尝试渲染,React期望的是组件(字符串/类/函数),实际得到的是数字,因此抛出类型错误。

之前环境能运行,大概率是因为当时图标是通过React组件形式导入(比如SVG转成的组件),而现在资源导入方式发生了变化(比如换成了普通图片资源,或SVG导入配置失效)。

另外,Route.js中的MainApp函数返回值多了一层大括号,这会导致语法错误,需要移除。

解决方法

情况1:图标是本地图片资源(png/jpg等)

修改TabItem.js,用React Native的Image组件包裹资源ID进行渲染:

import React from 'react'
import { StyleSheet, Text, TouchableOpacity, Image } from 'react-native' // 导入Image
import { IconAbout, IconAboutActive, IconHome, IconHomeActive, IconNearby, IconNearbyActive} from '../../assets'
import { WARNA_UTAMA, WARNA_DISABLE } from '../../utils/constant'

const TabItem = ({ isFocused, onPress, onLongPress, label }) => {
    const Icon = () => {
        let iconSource;
        if (label === 'Home') {
            iconSource = isFocused ? IconHomeActive : IconHome;
        } else if (label === 'Nearby') {
            iconSource = isFocused ? IconNearbyActive : IconNearby;
        } else if (label === 'About') {
            iconSource = isFocused ? IconAboutActive : IconAbout;
        } else {
            iconSource = IconHome;
        }
        // 用Image组件渲染资源ID,自定义宽高适配需求
        return <Image source={iconSource} style={{ width: 24, height: 24 }} />
      }
    return (
        <TouchableOpacity
            onPress={onPress}
            onLongPress={onLongPress}
            style={styles.container}>
            <Icon />
            <Text style={styles.text(isFocused)}>
              {label}
            </Text>
          </TouchableOpacity>
    )
}

export default TabItem

const styles = StyleSheet.create({
    container:{
        alignItems: 'center',
    },
    text: (isFocused) =>  ({
        fontSize: 13,
        color: isFocused ? WARNA_UTAMA : WARNA_DISABLE, 
    })
})

情况2:图标是SVG格式

如果原本是使用SVG组件,需要确保SVG文件被正确转换为React组件:

  1. 安装依赖:react-native-svg 和 react-native-svg-transformer
  2. 配置metro.config.js,让Metro能正确解析SVG文件为React组件
  3. 确保assets目录下的图标是.svg文件,且导入方式正确(直接导入即可作为组件使用)

修复Route.js的语法错误

移除MainApp返回值的多余大括号:

const MainApp = () => {
  return (
    <Tab.Navigator tabBar={props => <BottomNavigator {...props} />}>
      <Tab.Screen
        name="Home"
        component={Home}
        options={{headerShown: false}}
      />
      <Tab.Screen
        name="Nearby"
        component={Nearby}
        options={{headerShown: false}}
      />
      <Tab.Screen
        name="About"
        component={About}
        options={{headerShown: false}}
      />
    </Tab.Navigator>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 17:40:25