在 React Native 中将应用导航结构从版本 4 更改为 5 [英] Changing app navigation structure from version 4 to 5 in react native

查看:48
本文介绍了在 React Native 中将应用导航结构从版本 4 更改为 5的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 React 导航版本 4 开发一个旧应用程序,该应用程序包含一个注册和登录页面,然后是应用程序的内容.

I was working on an old app using react navigation version 4 the app contains a register and login in page obviously and then the content of the app.

最近我开始使用 React Navigation 版本 5 重新制作应用程序的内容,以便使用共享元素动画和底部选项卡导航器,这相当简单.

recently I started remaking the content of the app using react navigation version 5 in order to use the shared element animation and the bottom tab navigator and it was fairly simple.

但是我在将登录部分转换为版本 5 时遇到了困难,因为应用程序结构有些复杂,而且我对 React 导航版本 5 有点陌生.

but I struggled with converting the login part to version 5 since the app structure is somewhat complicated and I am somewhat new to react navigation version 5.

我将在下面留下一个应用程序结构图以及所使用的代码示例.

i will leave a figure of the app structure bellow a long with samples of the code used.

App.js:

import { setNavigator } from "./app/navigationRef";
const articleListFlow = createStackNavigator({
  Main: MainScreen, // screen with diffrent articles categories
  ResultsShow: ResultShowScreen, // article details screen
});
const loginFlow = createStackNavigator({
  Signup: SignupScreen,
  Signin: SigninScreen,
});
loginFlow.navigationOptions = () => {
  return {
    headerShown: false,
  };
};

articleListFlow.navigationOptions = {
  title: "News Feed",
  tabBarIcon: ({ tintColor }) => (
    <View>
      <Icon style={[{ color: tintColor }]} size={25} name={"ios-cart"} />
    </View>
  ),
  activeColor: "#ffffff",
  inactiveColor: "#ebaabd",
  barStyle: { backgroundColor: "#d13560" },
};
const switchNavigator = createSwitchNavigator({
  ResolveAuth: ResolveAuthScreen,
  MainloginFlow: createSwitchNavigator({
    //WelcomeScreen: WeclomeScreen,
    loginFlow: loginFlow,
  }),

  mainFlow: createMaterialBottomTabNavigator(
    {
      articleListFlow: articleListFlow,
      ArticleSave: ArticleSaveScreen, // we dont need this one
      Account: AccountScreen,
    },
    {
      activeColor: "#ffffff",
      inactiveColor: "#bda1f7",
      barStyle: { backgroundColor: "#6948f4" },
    }
  ),
});
const App = createAppContainer(switchNavigator);
export default () => {
  return (
    <AuthProvider>
      <App
        ref={(navigator) => {
          setNavigator(navigator);
        }}
      />
    </AuthProvider>
  );
};

NavigationRef.js:

NavigationRef.js :

import { NavigationActions } from "react-navigation";

let navigator;

export const setNavigator = (nav) => {
  navigator = nav;
};

export const navigate = (routeName, params) => {
  navigator.dispatch(
    NavigationActions.navigate({
      routeName,
      params,
    })
  );
};

// routename is the name of the routes singin singup accountscreen
// params information we want to pass to the screen we want to show

AuthContext.js

AuthContext.js

import { AsyncStorage } from "react-native";
import createDataContext from "./createDataContext";
import userAPI from "../api/user";

// using navigate to access the navigator and redirect the user
import { navigate } from "../navigationRef";

// AUTHENTICATION REDUCERS
const authReducer = (state, action) => {
  switch (action.type) {
    case "add_error": {
      return {
        ...state,
        errorMessage: action.payload,
      };
    }

    case "clear_error_message": {
      return {
        ...state,
        errorMessage: "",
      };
    }
    case "signin": {
      return {
        errorMessage: "",
        token: action.payload,
      };
    }

    default:
      return state;
  }
};

// CLEARING ERROR MESSAGES WHEN SWITCHING SIGNIN-SIGNUP
const clearErrorMessage = (dispatch) => () => {
  dispatch({ type: "clear_error_message" });
};

// AUTOMATIC SIGNIN ONLY USING TOKENS ON USER DEVICE
const tryLocalSignin = (dispatch) => async () => {
  const token = await AsyncStorage.getItem("token");
  if (token) {
    // if token exists
    dispatch({ type: "signin", payload: token });

    navigate("Main");
  } else {
    // if token doesnt exist
    navigate("WelcomeScreen");
  }
};

// SIGNUP
const signup = (dispatch) => async ({ email, password }) => {
  try {
    const response = await userAPI.post("/signup", { email, password });
    await AsyncStorage.setItem("token", response.data.token);
    dispatch({ type: "signin", payload: response.data.token });

    // making use of the navigate component to access navigation
    // and redirect the user
    navigate("Main");
  } catch (err) {
    dispatch({
      type: "add_error",
      payload: "Something went wrong with sign up",
    });
  }
};

// SIGNIN
const signin = (dispatch) => async ({ email, password }) => {
  try {
    const response = await userAPI.post("/signin", { email, password });
    await AsyncStorage.setItem("token", response.data.token);
    // using signin since the logic is the same
    dispatch({ type: "signin", payload: response.data.token });

    // making use of the navigate component to access navigation
    // and redirect the user
    navigate("Main");
  } catch (err) {
    console.log(err);
    dispatch({
      type: "add_error",
      payload: "Something went wrong with sign in",
    });
  }
};

// SIGNOUT
const signout = (dispatch) => async () => {
  // removing the token makes identification not work again
  await AsyncStorage.removeItem("token");
  dispatch({ type: "signout" });

  navigate("loginFlow");
};

// CREATING CONTEXT AND PROVIDER OBJECTS FOR AUTHENTICATION
export const { Provider, Context } = createDataContext(
  authReducer,
  {
    signin,
    signup,
    signout,
    clearErrorMessage,
    tryLocalSignin,
  },
  {
    token: null,
    errorMessage: "",
  }
);

createDataContext.js

createDataContext.js

import React, { useReducer } from "react";

export default (reducer, actions, defaultValue) => {
  const Context = React.createContext();

  const Provider = ({ children }) => {
    const [state, dispatch] = useReducer(reducer, defaultValue);

    const boundActions = {};

    for (let action in actions) {
      // for every action in the actions, call it with dispatch
      boundActions[action] = actions[action](dispatch);
    }

    return (
      <Context.Provider value={{ state, ...boundActions }}>
        {children}
      </Context.Provider>
    );
  };

  return { Context, Provider };
};

我对长代码表示歉意,并提前感谢任何可以提供帮助的人.

My appologies for the long code and thank you in advance for anyone who can help.

推荐答案

当从 V4 迁移到 V5 时,您需要考虑几件事,它涉及一些更改,您也可以考虑使用钩子等功能.

There are several things that you need to consider when moving from V4 to V5 it involves some changes and also you can consider using features like the hooks.

第一个更改将是移除 Switch Navigator 并有条件地在其位置渲染导航器.这将在您的 App.js 中完成.由于您已经有了一个基于 reducer 的实现,您可以使用状态值来做出这个决定.

The first change will be removing the Switch Navigator and conditionally render the navigator in its place. This will be done in your App.js. As you already have a reducer based implementation you can use the state values to take this decision.

下一个变化将是堆栈的创建,在 V4 中,您通过传递屏幕来创建导航,现在一切都是一个组件,您将屏幕作为子项传递.

The next change will be the creation of stacks, in V4 you create the navigation by passing the screen, now everything is a component and you pass the screens as children.

该选项也作为道具发送到导航器或屏幕本身.

The option are also sent as props to either the navigator or the screen itself.

导航参考的用法仍然是可能的,但您也可以在组件内部使用像 usenavigation 这样的钩子,并且对于您的身份验证流程,当您有条件地呈现导航器时,您不会使用它.

The usage of navigation ref is still possible but you can also use hooks like usenavigation inside components and for your authentication flow you wont be using this as you conditionally render the navigators.

我根据您的代码制作了一个简化版本.应用程序

I have made a simplified version based on your code. App.js

const AuthStack = createStackNavigator();
const AppTabs = createMaterialBottomTabNavigator();
const ArticleStack = createStackNavigator();

const Articles = () => {
  return (
    <ArticleStack.Navigator>
      <AppTabs.Screen name="ArticlesList" component={ArticleList} />
      <AppTabs.Screen name="ArticlesDetails" component={ArticleDetail} />
    </ArticleStack.Navigator>
  );
};

export default function App() {
  const [state, dispatch] = React.useReducer(authReducer, {
    isLoading: true,
    token: null,
    errorMessage: '',
  });

  React.useEffect(() => {
    const bootstrapAsync = async () => {
      const userToken = await AsyncStorage.getItem('userToken');
      dispatch({ type: 'RESTORE_TOKEN', token: userToken });
    };

    bootstrapAsync();
  }, []);
  const authContext = React.useMemo(
    () => ({
      signIn: async (data) => {
        dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
      },
      signOut: () => dispatch({ type: 'SIGN_OUT' }),
      signUp: async (data) => {
        dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
      },
    }),
    []
  );

  return (
    <AuthContext.Provider value={authContext}>
      <NavigationContainer>
        {state.token === null ? (
          <AuthStack.Navigator headerMode="none">
            {state.isLoading ? (
              <AuthStack.Screen name="Welcome" component={WelcomeScreen} />
            ) : (
              <>
                <AuthStack.Screen name="SignIn" component={SignInScreen} />
                <AuthStack.Screen name="SignUp" component={SingUpScreen} />
              </>
            )}
          </AuthStack.Navigator>
        ) : (
          <AppTabs.Navigator
            activeColor="#f0edf6"
            inactiveColor="#3e2465"
            barStyle={{ backgroundColor: '#694fad' }}>
            <AppTabs.Screen
              name="Articles"
              component={Articles}
              options={{
                tabBarLabel: 'Home',
                tabBarIcon: ({ color, size }) => (
                  <MaterialCommunityIcons
                    name="home"
                    color={color}
                    size={size}
                  />
                ),
              }}
            />
            <AppTabs.Screen name="Search" component={SearchScreen} />
            <AppTabs.Screen name="Save" component={SaveScreen} />
            <AppTabs.Screen name="Account" component={AccountScreen} />
          </AppTabs.Navigator>
        )}
      </NavigationContainer>
    </AuthContext.Provider>
  );
}

身份验证上下文

const AuthContext = React.createContext();
export default AuthContext;

认证减速器

export const authReducer = (state, action) => {
  switch (action.type) {
    case 'RESTORE_TOKEN':
      return {
        ...state,
        token: action.token,
        isLoading: false,
      };

    case 'SIGN_IN': {
      return {
        errorMessage: '',
        token: action.payload,
      };
    }

    case 'SIGN_OUT': {
      return {
        errorMessage: '',
        token: null,
      };
    }

    default:
      return state;
  }
};

如您所见,流程将显示欢迎屏幕,直到从异步存储加载令牌,然后基于该显示选项卡或登录屏幕.参数也作为道具传递.我已将操作移至 app.js,但也可以将其分开.

As you can see the flow will be showing the welcome screen till the token is loaded from async storage and then based on that show the tabs or the login screen. Also the parameters are passed as props. I've moved the actions to app.js but it can be separated as well.

您可以在此处查看完整运行的示例https://snack.expo.io/@guruparan/navigation-sample-3

You can see a fully running sample here https://snack.expo.io/@guruparan/navigation-sample-3

希望对您有所帮助,如有任何问题,请随时提问.

Hope this helps, Feel free to ask if there are any questions.

这篇关于在 React Native 中将应用导航结构从版本 4 更改为 5的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆