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 ofIcon.
此前代码在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组件:
- 安装依赖:
react-native-svg和react-native-svg-transformer - 配置
metro.config.js,让Metro能正确解析SVG文件为React组件 - 确保
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
相关产品推荐
相关产品推荐

