Expo React Native中Android/iOS端登出触发TypeError错误
问题
我开发的Expo React Native应用,网页端用户登出功能正常,但在Android和iOS端执行登出操作时,出现错误:TypeError: undefined is not a function,错误定位在BottomNavigation相关组件中。
用户首次进入应用及登录后流程均正常,已通过Firebase的onAuthStateChanged监听登录状态变化,状态变量loggedIn设置正确,但仍出现该登出错误。
App.js(省略导入部分)
const store = configureStore({ reducer: rootReducer }) const Stack = createStackNavigator() const Drawer = createDrawerNavigator() const App = ({ title }) => { const [loggedIn, setLoggedIn] = useState(false) const [loaded, setLoaded] = useState(false) const navigation = useNavigation() const openDrawer = () => { navigation.dispatch(DrawerActions.toggleDrawer()) } useEffect(() => { const unsubscribe = auth.onAuthStateChanged(user => { if (!user) { setLoggedIn(false) setLoaded(true) } else { setLoggedIn(user.emailVerified) setLoaded(true) } }) return unsubscribe }, []) if (!loaded) { return <Loader loading={loaded} /> } return !loggedIn ? ( <Stack.Navigator initialRouteName="Login"> <Stack.Screen name="Register" component={RegisterScreen} options={{ headerShown: false }} /> <Stack.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} /> <Stack.Screen name="ForgotPassword" component={ForgotPasswordScreen} options={{ headerShown: false }} /> </Stack.Navigator> ) : ( <Drawer.Navigator useLegacyImplementation drawerContent={props => <LogOut {...props} />} initialRouteName="Main" screenOptions={{ headerLeft: () => null, headerRight: ({ color, size = 26 }) => ( <MaterialCommunityIcons name="account" color={color} style={{ marginRight: 20 }} size={size} onPress={() => openDrawer()} /> ), drawerPosition: "right", }} > <Drawer.Screen name="Main" component={MainScreen} options={{ title: title, drawerItemStyle: { display: "none" }, }} /> <Drawer.Screen name="Califications" component={CalificationsScreen} options={{ headerShown: true, headerLeft: ({ color, size = 26 }) => ( <MaterialCommunityIcons name="arrow-left" color={color} style={{ marginLeft: 15 }} size={size} onPress={() => navigation.goBack()} /> ), title: "Calificaciones", }} /> <Drawer.Screen name="ChangePassword" component={ChangePassword} options={{ headerShown: true, headerLeft: ({ color, size = 26 }) => ( <MaterialCommunityIcons name="arrow-left" color={color} style={{ marginLeft: 15 }} size={size} onPress={() => navigation.goBack()} /> ), title: "Cambiar contraseña", }} /> </Drawer.Navigator> ) } const mapStateToProps = state => ({ title: state.navigationState.title, }) const ConnectedApp = connect(mapStateToProps)(App) const Root = () => { return ( <Provider store={store}> <NavigationContainer> <ConnectedApp /> </NavigationContainer> </Provider> ) } export default Root
Main.js
const Main = ({ fetchUser, saveTabTitle }) => { useEffect(() => { fetchUser() }, []) const Tab = createMaterialBottomTabNavigator() const handleTabPress = title => { saveTabTitle(title) } return ( <Tab.Navigator initialRouteName="Course" labeled={false} activeColor="#f0edf6" inactiveColor="#3e2465" > <Tab.Screen name="Course" component={CoursesScreen} listeners={{ tabPress: () => { handleTabPress("Cursos") }, }} options={{ tabBarIcon: ({ color, size = 26 }) => ( <MaterialCommunityIcons name="school" color={color} size={size} /> ), }} /> <Tab.Screen name="Calendar" component={CalendarScreen} listeners={{ tabPress: () => { handleTabPress("Calendario") }, }} options={{ tabBarIcon: ({ color, size = 26 }) => ( <MaterialCommunityIcons name="calendar" color={color} size={size} /> ), }} /> </Tab.Navigator> ) } const mapStateToProps = store => ({ currentUser: store.userState.currentUser, }) const mapDispatchToProps = dispatch => bindActionCreators({ fetchUser, saveTabTitle }, dispatch) export default connect(mapStateToProps, mapDispatchToProps)(Main)
解决方案
1. 将导航器创建移到组件外部
Native端对组件内部创建的导航器实例兼容性较差,登出卸载组件时容易出现引用失效问题。修改Main.js,把createMaterialBottomTabNavigator移到组件外部:
// 移到文件顶部,导入语句下方 const Tab = createMaterialBottomTabNavigator() const Main = ({ fetchUser, saveTabTitle }) => { useEffect(() => { fetchUser() }, []) const handleTabPress = title => { saveTabTitle(title) } // 原返回部分不变 }
2. 修复子页面headerLeft的navigation引用
App.js中Drawer子页面的headerLeft使用了App组件的全局navigation变量,登出时该导航实例已被卸载,导致调用goBack()时出现undefined错误。修改为从headerLeft的props中获取当前页面的navigation:
// 以CalificationsScreen为例,其他子页面同理 <Drawer.Screen name="Califications" component={CalificationsScreen} options={{ headerShown: true, headerLeft: ({ color, size = 26, navigation }) => ( <MaterialCommunityIcons name="arrow-left" color={color} style={{ marginLeft: 15 }} size={size} onPress={() => navigation.goBack()} /> ), title: "Calificaciones", }} />
3. 简化登出逻辑
检查LogOut组件的登出代码,确保只调用Firebase的signOut()方法,不要额外添加导航跳转逻辑——onAuthStateChanged会自动监听用户状态变化,更新loggedIn并触发App重新渲染为登录栈,避免手动导航导致的组件状态冲突。
4. 清理组件副作用(可选)
如果仍有问题,可在Main组件的useEffect中添加清理函数,避免登出后残留的异步操作触发回调:
useEffect(() => { const fetchUserPromise = fetchUser() return () => { // 如果fetchUser是异步操作,可在此标记组件已卸载,避免后续回调执行 } }, [fetchUser])
内容的提问来源于stack exchange,提问作者Pablo Martínez
相关产品推荐
相关产品推荐

