将导航传递给React Native中的App.js

3
我阅读了关于React Navigation的身份验证流程教程,并成功地将其集成到项目中。但有一件事我无法让它正常工作,那就是export default function App({ navigation })中的"navigation"部分。
我需要怎么做才能使它正常工作?我得到了navigation变量的undefined值。因为React Native Firebase notificationsNotifee,我需要在App.js中使用navigation。我的目标是监听前台和后台事件,并在收到通知并按下时导航到特定屏幕。如果没有navigation,我将无法实现这一目标。
以下是我的App.js:
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import LoginScreen from './src/screens/LoginScreen.js'
import HomeScreen from './src/screens/tabs/HomeScreen.js'
import LogoutScreen from './src/screens/tabs/LogoutScreen.js'
import ContactsScreen from './src/screens/tabs/home/ContactsScreen.js'
import ConfirmPhoneNumberScreen from './src/screens/ConfirmPhoneNumberScreen.js'
import { createStackNavigator } from '@react-navigation/stack';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { ActivityIndicator, View, Text, Alert, Platform } from "react-native";
import { AppStyles } from "./src/AppStyles";
import Auth from './src/helpers/Auth.js';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import NetInfo from "@react-native-community/netinfo";
import NoInternetScreen from './src/screens/NoInternetScreen.js';
import Trans from './src/helpers/Trans.js';
import Toast from 'react-native-toast-message';
import Firebase from './src/push_notifications/Firebase.js';
import messaging from '@react-native-firebase/messaging';
import LatestMessagesScreen from './src/screens/messages/LatestMessagesScreen.js';

const AuthContext = React.createContext();
const Stack = createStackNavigator();
const BottomTabs = createBottomTabNavigator();
const FirebaseClass = new Firebase();
const AuthClass = new Auth();

function App({navigation}) {

    const [state, dispatch] = React.useReducer(
        (prevState, action) => {
            switch (action.type) {
                case 'RESTORE_TOKEN':
                    return {
                        ...prevState,
                        userToken: action.token,
                        isLoading: false,
                    };
                case 'SIGN_IN':
                    return {
                        ...prevState,
                        isSignout: false,
                        userToken: action.token,
                    };
                case 'SIGN_OUT':
                    return {
                        ...prevState,
                        isSignout: true,
                        userToken: null,
                    };
                case 'INTERNET_IS_ON':
                    return {
                        ...prevState,
                        showNoInternetPage: false,
                    };
                case 'INTERNET_IS_OFF':
                    return {
                        ...prevState,
                        showNoInternetPage: true,
                    };
            }
        },
        {
            isLoading: true,
            isSignout: false,
            userToken: null,
            showNoInternetPage: false,
        }
    );

    React.useEffect(() => {

        const unsubscribe = NetInfo.addEventListener(state => {
            if (false === state.isInternetReachable && false === state.isConnected) {
                dispatch({ type: 'INTERNET_IS_OFF' });
            } else {
                dispatch({ type: 'INTERNET_IS_ON' });
            }
            //console.log("Connection type", state.isInternetReachable); //none
            //console.log("Is connected?", state.isConnected); //false
        });

        // Fetch the token from storage then navigate to our appropriate place
        const bootstrapAsync = async () => {
            let userToken;

            try {
                userToken = await AsyncStorage.getItem('@passwordAccessToken');
            } catch (e) {
                // Restoring token failed
            }
            // After restoring token, we may need to validate it in production apps

            // This will switch to the App screen or Auth screen and this loading
            // screen will be unmounted and thrown away.
            dispatch({ type: 'RESTORE_TOKEN', token: userToken });
        };

        bootstrapAsync();
        FirebaseClass.requestUserPermission();
        
    }, []);

    const authContext = React.useMemo(
        () => ({
            confirmPhoneNumber: async data => {
                data.navigation.navigate('ConfirmPhoneNumberScreen')
            },
            signIn: async data => {
                // In a production app, we need to send some data (usually username, password) to server and get a token
                // We will also need to handle errors if sign in failed
                // After getting token, we need to persist the token using `AsyncStorage`
                // In the example, we'll use a dummy token
                await AuthClass.getPasswordGrandTypeToken();
                const accessToken = await AsyncStorage.getItem('@passwordAccessToken');

                FirebaseClass.getPushToken();

                dispatch({ type: 'SIGN_IN', token: accessToken });
            },
            signOut: () => {
                AsyncStorage.removeItem('@passwordAccessToken');
                dispatch({ type: 'SIGN_OUT' })
            },
            signUp: async data => {
                // In a production app, we need to send user data to server and get a token
                // We will also need to handle errors if sign up failed
                // After getting token, we need to persist the token using `AsyncStorage`
                // In the example, we'll use a dummy token

                dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
            },
        }),
        []
    );

    createBottomTabs = () => {
        const { signOut } = React.useContext(AuthContext);

        return (
            <BottomTabs.Navigator>
                <BottomTabs.Screen name='HomeTab'
                    component={HomeScreen}
                    options={{
                        title: Trans.t('app.tabbar_home'),
                        tabBarIcon: ({ color, size }) => (
                            <MaterialCommunityIcons name="home" color={color} size={size} />
                        )
                    }}></BottomTabs.Screen>
                <BottomTabs.Screen name='LogoutTab' component={LogoutScreen}
                    options={{
                        title: Trans.t('app.tabbar_logout'),
                        tabBarIcon: ({ color, size }) => (
                            <MaterialCommunityIcons name="logout" color={color} size={size} />
                        )
                    }}
                    listeners={{
                        tabPress: e => {
                            e.preventDefault();
                            Alert.alert(   // Shows up the alert without redirecting anywhere
                                Trans.t('alert.confirmation_title'),
                                Trans.t('alert.confirmation_body'),
                                [
                                    { text: Trans.t('alert.yes'), onPress: () => { signOut(); } },
                                    { text: Trans.t('alert.no') }
                                ]
                            );
                        }
                    }}
                ></BottomTabs.Screen>
            </BottomTabs.Navigator>
        )
    }


    if (state.isLoading) {
        // We haven't finished checking for the token yet
        return (
            <ActivityIndicator
                style={{ marginTop: 200 }}
                size="large"
                color={AppStyles.color.tint}
            />
        );
    }
    
    return (
        <NavigationContainer>
            <AuthContext.Provider value={authContext}>
                <Stack.Navigator>
                    {(state.showNoInternetPage) ?
                        <>
                            <Stack.Screen
                                name="NoInternet"
                                component={NoInternetScreen}
                                options={{
                                    headerTitle: Trans.t('app.no_internet_header')
                                }}
                            ></Stack.Screen>

                        </> : (state.userToken == null) ? (
                            <>
                                <Stack.Screen name="Login" component={LoginScreen}
                                    options={{
                                        headerTitle: Trans.t('app.login_or_register_header'),
                                        headerTitleAlign: 'center'
                                    }} />
                                <Stack.Screen
                                    name="ConfirmPhoneNumberScreen"
                                    component={ConfirmPhoneNumberScreen}
                                    options={{ headerTitle: Trans.t('app.confirm_phone_number_header') }} />
                            </>
                        ) : (
                                <>
                                    <Stack.Screen name="Home"
                                        children={createBottomTabs}
                                        options={{
                                            headerTitle: Trans.t('app_name'),
                                            headerTitleAlign: 'center'
                                        }}></Stack.Screen>

                                    <Stack.Screen
                                        name='Contacts'
                                        component={ContactsScreen}
                                    ></Stack.Screen>

                                    <Stack.Screen
                                    name='LatestMessages'
                                    options={{
                                        headerTitle: Trans.t('latest_messages_screen.header_title')
                                    }}
                                    component={LatestMessagesScreen}
                                    ></Stack.Screen>
                                </>
                            )}
                </Stack.Navigator>
                <Toast ref={(ref) => Toast.setRef(ref)} />
            </AuthContext.Provider>
        </NavigationContainer>
    );
}

export default App;
export { AuthContext };

这是我的index.js文件

import { registerRootComponent } from 'expo';

import App from './App';

// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in the Expo client or in a native build,
// the environment is set up appropriately
registerRootComponent(App);

有人能帮我解决这个问题吗?


请贴上相关的代码。 - Ketan Ramteke
当然,给你。:) - FosAvance
不确定,但你试过导出默认函数吗?{ const navigation = nav; return App(navigation);} 或者在index.js中 const expoApp = App(navigation); registerRootComponent(expoApp); - JB_DELR
1个回答

4

首先创建一个RootNavigation,然后将其导出为您的导航类。

RootNavigation.js

import * as React from 'react';

export const isReadyRef = React.createRef();

export const navigationRef = React.createRef();

export function navigate(name, params) {
  if (isReadyRef.current && navigationRef.current) {
    // Perform navigation if the app has mounted
    navigationRef.current.navigate(name, params);
  } else {
    // You can decide what to do if the app hasn't mounted
    // You can ignore this, or add these actions to a queue you can call later
  }
}

一旦你具备了上述要求,你需要在你的应用中引用它。

import { navigationRef, isReadyRef } from './RootNavigation';

React.useEffect(() => {
    return () => {
      isReadyRef.current = false
    };
  }, []);
return (
        <NavigationContainer ref={navigationRef}
                             onReady={() => {
                                  isReadyRef.current = true;
          }}>
            <AuthContext.Provider value={authContext}>

一旦你完成上述步骤,你就可以在应用程序中的任何地方使用它,包括像App.js这样的文件:
import * as RootNavigation from './path/to/RootNavigation.js';

// ...

RootNavigation.navigate('ChatScreen', { userName: 'Lucy' });

source: https://reactnavigation.org/docs/navigating-without-navigation-prop


我遇到了这个错误:./utils/navigation/RootNavigation").navigationRef.navigate' 未定义。 - famfamfam
1
应该使用 navigationRef.current.navigate,而不是 navigationRef.navigate - Wen W

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接