React Native Экран навигации React не перенаправляет после входа в систему с помощью AsyncStorage

Итак, я создал систему входа в систему с React Native, React Navigation и AsyncStorage. Если пользователь нажимает кнопку, он входит в систему, и значение AsyncStorage Key @loginuser обновляется. Теперь я ожидал, что экран автоматически обновится, но мне нужно закрыть приложение и запустить его снова - это не оптимально.

(Я также видел React-Native / React перенаправление навигации после входа в систему с redux, но он очень старый)

App.js

import React from 'react';
import { Text, View, StyleSheet, Button, TouchableOpacity, TextInput } from 'react-native';
import DefaultStackNavigation from './components/Navigation/Navigation';


const App = () => {
  return(
    <View>
      <DefaultStackNavigation />
    </View>
  )
}

export default App;

Navigation.js

import React, {useEffect, useState} from 'react';
//React Native
import { Text, View, StyleSheet} from 'react-native';
//Screens
import HomeScreen from '../HomeScreen/HomeScreen'
import AddScreen from "../AddScreen/AddScreen";
import NotificationScreen from "../NotificationScreen/NotificationScreen";
import MenuScreen from "../MenuScreen/MenuScreen";
import SearchScreen from "../SearchScreen/SearchScreen";
import PostJobScreen from "../PostJobScreen/PostJobScreen";
import JobOfferScreen from "../JobOfferScreen/JobOfferScreen";
import ProfileScreen from "../ProfileScreen/ProfileScreen";
import NoneLoggedinScreen from "../NoneLoggedinScreen/NoneLoggedinScreen"
import SignupModal from "../NoneLoggedinScreen/SignupModal"
//React Navigation
import { createStackNavigator } from "@react-navigation/stack";
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
//Third Party
import AsyncStorage from '@react-native-async-storage/async-storage';




const Tab = createBottomTabNavigator();

//Tab bar
const HomeTabs = () => {
  return (
    <Tab.Navigator tabBarOptions={{style:{height: 50}}, {showIcon: true}, {showLabel: false}} >
      <Tab.Screen name="Home" component={HomeScreen}/>
      <Tab.Screen name="SearchScreen" component={SearchJobStack}/>
      <Tab.Screen name="AddScreen" component={AddScreen}/>
      <Tab.Screen name="NotificationScreen" component={NotificationScreen} />}}/>
      <Tab.Screen name="MenuScreen" component={MenuScreen}/>}}/>
    </Tab.Navigator>
  );
}

const Stack = createStackNavigator();
const STORAGE_KEY = '@loginStatus'

const DefaultStackNavigation = () => {

  const [loginStatus, setLoginStatus] = useState()
  const readData = async () => {
    try {
      const isLoggedIn = JSON.parse(await AsyncStorage.getItem(STORAGE_KEY))
      console.log(isLoggedIn)
      if (isLoggedIn !== null) {
        setLoginStatus(isLoggedIn)
      }
    } catch (e) {
      alert('Failed to fetch the data from storage')
    }
  }

  readData()

  return loginStatus ? (
    <NavigationContainer>
      <Stack.Navigator screenOptions={{headerShown: false}} independent={false}>
        <Stack.Screen name="HomeTabs" component={HomeTabs} />
        <Stack.Screen name="PostJobScreen" component={PostJobScreen} />
        <Stack.Screen name="ProfileScreen" component={ProfileScreen}/>
      </Stack.Navigator>
    </NavigationContainer>
  ) : (
    <NavigationContainer>
      <Stack.Navigator screenOptions={{headerShown: false}} independent={false}>
        <Stack.Screen name="NoneLoggedinScreen" component={NoneLoggedinScreen} />
        <Stack.Screen name="SignupModal" component={SignupModal} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};


export default DefaultStackNavigation;

Нет

import React, { useState, useEffect } from 'react';
import { Text, View, Button} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';

const STORAGE_KEY = '@loginStatus'

const SignupModal = () => {

  const [loginStatus, setLoginStatus] = useState(false)
  const saveData = async (parmLoginStatus) => {
    try {
      await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(parmLoginStatus))
      alert('Data successfully saved -> Logged In')
      console.log(loginStatus)
    } catch (e) {
      alert('Failed to save the data to the storage')
    }
  }

  const onSubmitLogin = () => {
    setLoginStatus(true)
    saveData(true)
  }


  return(
    <View>
      <Text>Login Page. Press the button to log in and stay logged in</Text>
      <Button title="Log in" onPress={() => onSubmitLogin()}/>
    </View>
  );
};

const styles = StyleSheet.create({
  App: {
    flex: 1,
    backgroundColor: "white"
  },
});

export default SignupModal;

Теперь я ожидал, что страница перезагрузится, и я могу использовать приложение. К сожалению, это не так. Данные сохранены, а статус входа установлен на true, но мне нужно перезапустить приложение, чтобы использовать его. Коварство в том, что если я напишу логику входа AsyncStorage из файла NoneLoggedinScreen.js в файл App.js, приложение будет работать нормально - ›Однако это не альтернатива для меня, потому что общая структура приложения, я думаю, , построенный относительно разумно. Также, когда пользователь пытается быть перенаправлен вручную (с помощью кнопки) с помощью navigation.navigate (HomeTabs) после входа в систему не работает, и я получаю сообщение об ошибке, что Home не существует, что также непонятно, потому что в навигации есть на самом деле было установлено значение «Войти сейчас». У кого-нибудь была такая проблема?

Это мои зависимости, кстати, зависимости: {
@ response-native-async-storage / async-storage: ^ 1.15.1,
@ response-native-community / masked-view: ^ 0.1.10,
@ react-navigation / bottom-tabs: ^ 5.11.7,
@ response-navigation / native: ^ 5.9.2,
@ response-navigation / stack: ^ 5.14.2,
react: 16.13.1,
react-native: 0.63.4,
response-native-gesture-handler: ^ 1.10.1,
react-native-reanimated: ^ 1.13.2,
response-native-safe-area-context: ^ 3.1.9,
реагировать-native-screen: ^ 2.17.1,
},


person Henrik    schedule 13.04.2021    source источник


Ответы (2)


В вашей стратегии аутентификации есть скрытое предположение, что когда вы сохраняете данные с помощью AsyncStorage в NoneLoggedInScreen, данные будут немедленно считаны с помощью функции readData в DefaultStackNavigation. Причина, по которой этого не происходит, заключается в том, что readData вызывается только при рендеринге / повторном рендеринге DefaultStackNavigation. Этого не произойдет, если один из его дочерних элементов установит некоторые данные в локальное хранилище.

Есть много способов исправить это. Использование redux с redux-persist или другой настройкой управления состоянием и сохранением, или специально созданное решение на основе контекста (см., Например, эту статью Kent C. Dodds на Аутентификация в приложениях React).


Вторая, незначительная проблема, о которой вы упомянули:

navigation.navigate (HomeTabs) после входа в систему не работает, и я получаю сообщение об ошибке Home не существует

имеет смысл, и причина этого в том, что вы условно визуализируете два разных NavigationContainers. Они не знают о маршрутах друг друга, поэтому вы не можете перемещаться между ними.

Чтобы исправить это, вы должны отобразить один NavigationContainer и условно отобразить один из Stack.Navigator в качестве его дочернего элемента.

person Marek Lisik    schedule 13.04.2021
comment
Спасибо. Как вы думаете, это нормальный способ сделать это? - person Henrik; 13.04.2021
comment
Что ж, это более широкий архитектурный выбор - в случае redux / mobx и т.п. он будет определять, как вы пишете управление состоянием, компоненты и бизнес-логику. Возможно, вы захотите просмотреть несколько вариантов и выбрать наиболее подходящий. - person Marek Lisik; 13.04.2021
comment
Окей. Спасибо! :) - person Henrik; 13.04.2021

Чтобы управлять потоком аутентификации беспрепятственно, мы должны сделать два отдельных навигатора для навигации. Я считаю этот рабочий процесс лучшим на данный момент. Он отлично работает и чистый. Итак, что я обычно делаю в своих проектах, я создаю два навигатора AuthNavigator.js и AppNavigator.js, а затем использую условный рендеринг.

Итак, что вы можете сделать, это создать папку с именем navigation, в которой находится ваш App.js. Затем внутри папки navigation создайте два файла с именами AppNavigator.js и AuthNavigator.js.

Ваш AuthNavigator.js должен выглядеть так

import React from "react";
import NoneLoggedinScreen from "../NoneLoggedinScreen/NoneLoggedinScreen";
import SignupModal from "../NoneLoggedinScreen/SignupModal";
import { createStackNavigator } from "@react-navigation/stack";

const Stack = createStackNavigator();

const AuthNavigator = () => {
  return (
    <Stack.Navigator screenOptions={{ headerShown: false }} independent={false}>
      <Stack.Screen name="NoneLoggedinScreen" component={NoneLoggedinScreen} />
      <Stack.Screen name="SignupModal" component={SignupModal} />
    </Stack.Navigator>
  );
};

export default AuthNavigator;

Ваш AppNavigator.js должен выглядеть так

import React from "react";
import HomeScreen from "../HomeScreen/HomeScreen";
import AddScreen from "../AddScreen/AddScreen";
import NotificationScreen from "../NotificationScreen/NotificationScreen";
import MenuScreen from "../MenuScreen/MenuScreen";
import PostJobScreen from "../PostJobScreen/PostJobScreen";
import ProfileScreen from "../ProfileScreen/ProfileScreen";
import { createStackNavigator } from "@react-navigation/stack";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";

const Tab = createBottomTabNavigator();

//Tab bar
const HomeTabs = () => {
  return (
    <Tab.Navigator
      tabBarOptions={
        ({ style: { height: 50 } }, { showIcon: true }, { showLabel: false })
      }
    >
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="SearchScreen" component={SearchJobStack} />
      <Tab.Screen name="AddScreen" component={AddScreen} />
      <Tab.Screen name="NotificationScreen" component={NotificationScreen} />
      <Tab.Screen name="MenuScreen" component={MenuScreen} />
    </Tab.Navigator>
  );
};

const Stack = createStackNavigator();

const AppNavigator = () => {
  return (
    <Stack.Navigator screenOptions={{ headerShown: false }} independent={false}>
      <Stack.Screen name="HomeTabs" component={HomeTabs} />
      <Stack.Screen name="PostJobScreen" component={PostJobScreen} />
      <Stack.Screen name="ProfileScreen" component={ProfileScreen} />
    </Stack.Navigator>
  );
};

export default AppNavigator;

И ваш App.js должен выглядеть так

import React, { useState, useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native';

import AuthStorage from './auth/storage';
import AppNavigator from './navigation/AppNavigator';
import AuthNavigator from './navigation/AuthNavigator';

const App = () => {
  const [loginStatus, setLoginStatus] = useState(false);

  // I am using useEffect hook but I would prefer using `AppLoading` to restore token if it exists
  useEffect(() => {
    readData();
  }, []);

  const readData = async () => {
    try {
      const isLoggedIn = JSON.parse(await AuthStorage.getItem());
      console.log(isLoggedIn);
      if (isLoggedIn !== null) {
        setLoginStatus(isLoggedIn);
      }
    } catch (e) {
      alert('Failed to fetch the data from storage');
    }
  };

  return (
    <NavigationContainer>
      {loginStatus ? <AppNavigator /> : <AuthNavigator />}
    </NavigationContainer>
  );
};

export default App;

Создайте папку с именем auth, в которой находится ваш App.js. Внутри создайте файл с именем storage.js. Теперь внутри storage.js вставьте этот код

import AsyncStorage from '@react-native-async-storage/async-storage';

const AuthToken = '@loginStatus';

const storeItem = async (value) => {
  try {
    await AsyncStorage.setItem(AuthToken, JSON.stringify(value));
    return true;
  } catch (e) {
    return false;
  }
};

const getItem = async () => {
  try {
    const jsonValue = await AsyncStorage.getItem(AuthToken);
    return jsonValue != null ? JSON.parse(jsonValue) : null;
  } catch (e) {
    return null;
  }
};

export default { storeItem, getItem };
person Kartikey    schedule 13.04.2021
comment
У меня все та же проблема: / - person Henrik; 13.04.2021
comment
Попробуйте эту закуску. Я реализовал его, и он отлично работает. - person Kartikey; 13.04.2021
comment
Да, в целом он работает нормально, но если я изменю значение @loginStatus для каждой кнопки или что-то еще, оно не будет обновляться - person Henrik; 13.04.2021