React Native Tab View页面跳转报错:navigation.navigate未定义
问题描述
在React Native项目中,从MyCoursesPage页面的React Native Tab View路由组件(FirstRoute、SecondRoute)跳转到MyCoursesPage2时,点击onPress触发错误:undefined is an object (evaluating navigation.navigate)。外层MyCoursesPage组件内的onPress可正常工作,但Tab View的路由组件无法获取navigation对象。已配置对应的Stack Navigator。
相关代码
MyCoursesPage代码
import React, {useState} from "react"; import { View, StyleSheet, TouchableOpacity, Image, Dimensions, ScrollView, StatusBar, Text } from 'react-native'; import { SafeAreaView } from "react-native-safe-area-context"; import CustomHeader from "../../components/CustomHeader"; import { IMAGE } from "../../constants/Images"; import { TabView, SceneMap, TabBar } from 'react-native-tab-view'; import * as Progress from 'react-native-progress'; const FirstRoute = ({navigation}) => ( <ScrollView> <View style={[styles.scene, { backgroundColor: '#f7f7f7' }]} /> </ScrollView> ); function SecondRoute({ navigation }) { return ( <ScrollView> <View style={[styles.scene, { backgroundColor: '#f7f7f7' }]} /> <TouchableOpacity onPress={() => navigation.navigate("MyCoursesPage2")}> <View style={{ borderWidth: 1, borderColor: "#fff", backgroundColor:'#fff', borderRadius: 10, padding: 15, marginHorizontal:20, marginVertical: 15, display:'flex', flexDirection:'row' }} > <Image source={IMAGE.MYCOURSES3} style={{ width: 60, height: 60 }} /> <View> <Text style={{fontSize:17, fontWeight:'bold', marginHorizontal:15}}>3D Design Illustration</Text> <Text style={{fontSize:14, color:'grey', marginHorizontal:15}}>2hrs 25mins</Text> <Progress.Bar style={{marginHorizontal:15, marginVertical:5}} progress={1} width={200} /> </View> </View> </TouchableOpacity> <TouchableOpacity> <View style={{ borderWidth: 1, borderColor: "#fff", backgroundColor:'#fff', borderRadius: 10, padding: 15, marginHorizontal:20, display:'flex', flexDirection:'row' }} > <Image source={IMAGE.MYCOURSES4} style={{ width: 60, height: 60 }} /> <View> <Text style={{fontSize:17, fontWeight:'bold', marginHorizontal:15}}>CRM Management</Text> <Text style={{fontSize:14, color:'grey', marginHorizontal:15}}>3hrs 20mins</Text> <Progress.Bar style={{marginHorizontal:15, marginVertical:5}} progress={0.88} width={200} /> </View> </View> </TouchableOpacity> <TouchableOpacity> <View style={{ borderWidth: 1, borderColor: "#fff", backgroundColor:'#fff', borderRadius: 10, padding: 15, marginHorizontal:20, marginVertical:15, display:'flex', flexDirection:'row' }} > <Image source={IMAGE.MYCOURSES5} style={{ width: 60, height: 60 }} /> <View> <Text style={{fontSize:17, fontWeight:'bold', marginHorizontal:15}}>Flutter Mobile Apps</Text> <Text style={{fontSize:14, color:'grey', marginHorizontal:15}}>4hrs 50mins</Text> <Progress.Bar style={{marginHorizontal:15, marginVertical:5}} progress={0.95} width={200} /> </View> </View> </TouchableOpacity> <TouchableOpacity> <View style={{ borderWidth: 1, borderColor: "#fff", backgroundColor:'#fff', borderRadius: 10, padding: 15, marginHorizontal:20, display:'flex', flexDirection:'row' }} > <Image source={IMAGE.MYCOURSES6} style={{ width: 60, height: 60 }} /> <View> <Text style={{fontSize:17, fontWeight:'bold', marginHorizontal:15}}>3D Icons Set Blender</Text> <Text style={{fontSize:14, color:'grey', marginHorizontal:15}}>2hrs 45mins</Text> <Progress.Bar style={{marginHorizontal:15, marginVertical:5}} progress={0.85} width={200} /> </View> </View> </TouchableOpacity> </ScrollView> ) } const initialLayout = { width: Dimensions.get('window').width }; function MyCoursesPage({ navigation }) { const [index, setIndex] = React.useState(0); const [routes] = React.useState([ { key: 'first', title: 'Ongoing' }, { key: 'second', title: 'Completed' }, ]); const renderScene = SceneMap({ first: FirstRoute, second: SecondRoute, }); return ( <SafeAreaView style={{ flex: 1, backgroundColor:'#f7f7f7' }}> <CustomHeader title="My Courses" isHome={true} navigation={navigation} /> <View style={{ flexDirection: "row", justifyContent: "flex-end", marginTop:-35 }}> <TouchableOpacity > <Image source={IMAGE.MYCOURSES1} style={{width: 22, height:22}}/> </TouchableOpacity> <TouchableOpacity > <Image source={IMAGE.MYCOURSES2} style={{width: 22, height:22, marginHorizontal:15}}/> </TouchableOpacity> </View> <TabView navigationState={{ index, routes }} renderTabBar={props => ( <TabBar {...props} renderLabel={({ route, color }) => ( <Text style={{ color: '#335ef7', fontWeight:'bold', margin: 8 }}> {route.title} </Text> )} style={{backgroundColor: 'white'}} /> )} renderScene={renderScene} onIndexChange={setIndex} initialLayout={initialLayout} style={styles.container} /> </SafeAreaView> ); } export default MyCoursesPage; const styles = StyleSheet.create({ container: { marginTop: StatusBar.currentHeight, }, scene: { flex: 1, }, });
Stack Navigator代码
import React from "react"; import { createStackNavigator } from "@react-navigation/stack"; import MyCoursesPage from "../pages/MyCourses/MyCoursesPage"; import MyCoursesPage2 from "../pages/MyCourses/MyCoursesPage2" import MyCoursesDetailsPage from "../pages/MyCourses/MyCoursesDetailsPage"; import Coursedetailscreen from "../pages/MyCourses/Coursedetailscreen"; const StackSetting = createStackNavigator(); function MyCoursesStackNavigator() { return ( <StackSetting.Navigator initialRouteName="MyCoursesPage" screenOptions={{ headerShown: false }}> <StackSetting.Screen name="MyCoursesPage" component={MyCoursesPage} /> <StackSetting.Screen name="MyCoursesPage2" component={MyCoursesPage2} /> <StackSetting.Screen name="MyCoursesDetailsPage" component={MyCoursesDetailsPage} /> <StackSetting.Screen name="Coursedetailscreen" component={Coursedetailscreen} /> </StackSetting.Navigator> ); } export default MyCoursesStackNavigator;
解决方案
方法1:自定义renderScene传递navigation
SceneMap默认不会将外层的navigation对象传递给Tab View的子路由组件,需要手动自定义renderScene函数,将navigation作为props传入:
修改MyCoursesPage中的renderScene部分:
// 替换原有的const renderScene = SceneMap(...) const renderScene = ({ route }) => { switch (route.key) { case 'first': return <FirstRoute navigation={navigation} />; case 'second': return <SecondRoute navigation={navigation} />; default: return null; } };
修改后,FirstRoute和SecondRoute就能正确接收navigation props,调用navigation.navigate即可正常跳转。
方法2:使用useNavigation钩子获取导航对象
如果不想修改renderScene,可以在子路由组件中直接使用React Navigation提供的useNavigation钩子来获取navigation对象:
首先在子组件中导入useNavigation:
import { useNavigation } from '@react-navigation/native';
然后修改FirstRoute和SecondRoute组件:
const FirstRoute = () => { const navigation = useNavigation(); return ( <ScrollView> <View style={[styles.scene, { backgroundColor: '#f7f7f7' }]} /> </ScrollView> ); }; function SecondRoute() { const navigation = useNavigation(); return ( <ScrollView> <View style={[styles.scene, { backgroundColor: '#f7f7f7' }]} /> <TouchableOpacity onPress={() => navigation.navigate("MyCoursesPage2")}> {/* 原有组件内容 */} </TouchableOpacity> {/* 其他组件内容 */} </ScrollView> ) }
这种方法无需手动传递navigation,实现更简洁,适合大多数场景。
内容的提问来源于stack exchange,提问作者Vaibhav Thakur
相关产品推荐
相关产品推荐

