Obsługa montowania i odłączania tras nawigacyjnych w React Native

Opublikowany: 2022-03-10
Szybkie podsumowanie ↬ Często potrzebne są dwa różne zestawy stosów nawigacji do uwierzytelniania przed i po użytkowniku. Zwykle, aby zobaczyć więcej treści, musisz zostać w jakiś sposób uwierzytelniony. Przyjrzyjmy się, jak montować i odmontowywać stos nawigacji w oparciu o spełniony warunek w React Native.

W tym artykule omówimy montowanie i odmontowywanie tras nawigacyjnych w React Native. Oczekiwane zachowanie aplikacji polega na tym, że po spełnieniu warunku uwierzytelnienia nowy zestaw tras nawigacji jest dostępny tylko dla zalogowanych użytkowników, podczas gdy inne ekrany, które były wyświetlane przed uwierzytelnieniem, są usuwane i nie można do nich powrócić, chyba że użytkownik wyloguje się z aplikacji.

Ze względów bezpieczeństwa w Twojej aplikacji chronione trasy umożliwiają wyświetlanie tylko określonych informacji/treści w aplikacji określonym użytkownikom, jednocześnie ograniczając dostęp osobom nieupoważnionym.

Będziemy współpracować z Expo nad tym projektem, ponieważ pomoże nam to skoncentrować się na bieżącym problemie, zamiast martwić się o wiele konfiguracji. Dokładnie te same kroki opisane w tym artykule można wykonać dla samej aplikacji React Native.

Aby zapoznać się z tym samouczkiem, potrzebujesz trochę znajomości JavaScript i React Native . Oto kilka ważnych rzeczy, które powinieneś już znać:

  • Komponenty niestandardowe w React Native (jak tworzyć komponenty, otrzymywać, przekazywać i używać właściwości w komponencie). Czytaj więcej.
  • Reaguj nawigację. Czytaj więcej.
  • Stack Navigator w React Native. Czytaj więcej.
  • Podstawowa znajomość komponentów React Native Core ( <View/> , <Text/> , itp.). Czytaj więcej.
  • React Native AsyncStorage . Czytaj więcej.
  • Kontekstowy interfejs API. Czytaj więcej.

Konfiguracja projektu i uwierzytelnianie podstawowe

Jeśli jesteś nowy w używaniu expo i nie wiesz, jak zainstalować expo, odwiedź oficjalną dokumentację. Po zakończeniu instalacji przejdź dalej, aby zainicjować nowy projekt React Native z expo z naszego wiersza poleceń:

 expo init navigation-project

Zostaną wyświetlone opcje umożliwiające wybór sposobu, w jaki ma wyglądać podstawowa konfiguracja:

Podstawowa konfiguracja projektu React Native
(duży podgląd)

W naszym przypadku wybierzmy pierwszą opcję, aby ustawić nasz projekt jako pusty dokument. Teraz poczekaj, aż instalacja zależności JavaScript zostanie zakończona.

Po skonfigurowaniu naszej aplikacji możemy zmienić nasz katalog na nasz nowy katalog projektu i otworzyć go w ulubionym edytorze kodu. Musimy zainstalować bibliotekę, której będziemy używać dla AsyncStorage i naszych bibliotek nawigacyjnych. W swoim katalogu folderów w terminalu wklej powyższe polecenie i wybierz szablon ( blank zadziała), aby zainstalować nasze zależności projektu.

Przyjrzyjmy się, do czego służy każda z tych zależności:

  • @react-native-community/async-storage
    Podobnie jak localStorage w sieci, jest to interfejs API React Native do utrwalania danych na urządzeniu w parach klucz-wartość.
  • @react-native-community/widok-maskowany, ekrany natywne reakcji, uchwyt-gestu-react-native
    Te zależności to podstawowe narzędzia używane przez większość nawigatorów do tworzenia struktury nawigacji w aplikacji. (Przeczytaj więcej w Rozpoczęcie pracy z nawigacją React Native.)
  • @react-navigation/native
    To jest zależność nawigacji React Native.
  • @react-nawigacja/stos
    Jest to zależność nawigacji stosu w React Native.
 npm install @react-native-community/async-storage @react-native-community/masked-view @react-navigation/native @react-navigation/stack react-native-screens react-native-gesture-handle

Aby uruchomić aplikację, użyj expo start z katalogu aplikacji w swoim terminalu. Po uruchomieniu aplikacji możesz użyć aplikacji expo z telefonu komórkowego, aby zeskanować kod kreskowy i wyświetlić aplikację, a jeśli masz emulator Androida/symulator IOS, możesz otworzyć aplikację za ich pośrednictwem z narzędzia programisty expo, które otwiera się w przeglądarce po uruchomieniu aplikacji expo. W przypadku przykładów obrazów w tym artykule użyjemy Genymotions, aby zobaczyć nasz wynik. Oto jak będzie wyglądał nasz końcowy wynik w Genymotions:

wynik końcowy w Genymotions
(duży podgląd)

Struktury folderów

Stwórzmy naszą strukturę folderów od samego początku, aby łatwiej było nam z nią pracować:

Najpierw potrzebujemy dwóch folderów:

  • kontekst
    Ten folder będzie zawierał kontekst dla całej naszej aplikacji, ponieważ będziemy pracować z Context API dla globalnego zarządzania stanem.
  • wyświetlenia
    Ten folder będzie zawierał zarówno folder nawigacyjny, jak i widoki dla różnych ekranów.

Śmiało i utwórz dwa foldery w katalogu projektu.

Wewnątrz folderu kontekstowego utwórz folder o nazwie authContext i utwórz dwa pliki wewnątrz folderu authContext :

  • AuthContext.js ,
  • AuthState.js .

Pliki te będą nam potrzebne, gdy zaczniemy pracować z Context API.

Teraz przejdź do folderu widoków , który utworzyliśmy i utwórz w nim jeszcze dwa foldery, a mianowicie:

  • nawigacja ,
  • ekrany .

Teraz jeszcze nie skończyliśmy, w folderze screens utwórz te dwa kolejne foldery:

  • postAuthScreen ,
  • PreAuthScreen .

Jeśli prawidłowo postępowałeś zgodnie z konfiguracją folderów, tak powinna wyglądać obecnie struktura folderów:

struktura folderów
(duży podgląd)
Więcej po skoku! Kontynuuj czytanie poniżej ↓

Tworzenie naszego pierwszego ekranu

Teraz utwórzmy nasz pierwszy ekran i nazwijmy go welcomeScreen.js w folderze preAuthScreens .

preAuthScreens > welcomeScreen.js

Oto zawartość naszego pliku welcomeScreen.js :

 import React from 'react'; import { View, Text, Button, StyleSheet, TextInput } from 'react-native'; const WelcomeScreen = () => { const onUserAuthentication = () => { console.log("User authentication button clicked") } return ( <View style={styles.container}> <Text style={styles.header}>Welcome to our App!</Text> <View> <TextInput style={styles.inputs} placeholder="Enter your email here.." /> <TextInput style={styles.inputs} secureTextEntry={true} placeholder="Enter your password here.." /> <Button title="AUTHENTICATE" onPress={onUserAuthentication} /> </View> </View> ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }, header: { fontSize: 25, fontWeight: 'bold', marginBottom: 30 }, inputs: { width: 300, height: 40, marginBottom: 10, borderWidth: 1, } }) export default WelcomeScreen

Oto, co zrobiliśmy w powyższym bloku kodu:

Najpierw zaimportowaliśmy potrzebne nam rzeczy z biblioteki React Native, a mianowicie View , Text , Button , TextInput . Następnie stworzyliśmy nasz funkcjonalny komponent WelcomeScreen .

Zauważysz, że zaimportowaliśmy StyleSheet stylów z React Native i użyliśmy go do zdefiniowania stylów naszego nagłówka, a także naszego <TextInput /> .

Na koniec eksportujemy komponent WelcomeScreen na dole kodu.

Teraz, gdy już z tym skończyliśmy, sprawmy, aby ten komponent działał zgodnie z oczekiwaniami, używając zaczepu useState do przechowywania wartości danych wejściowych i aktualizowania ich stanów za każdym razem, gdy nastąpi zmiana w polach wejściowych. Wprowadzimy również import useCallback z Reacta, ponieważ będziemy go później potrzebować do przechowywania funkcji.

Po pierwsze, gdy wciąż jesteśmy w komponencie WelcomeScreen , musimy zaimportować useState i useCallback z Reacta.

 import React, { useState, useCallback } from 'react';

Teraz w komponencie funkcjonalnym WelcomeScreen stwórzmy dwa stany odpowiednio dla adresu e-mail i hasła:

 ... const WelcomeScreen = () => { const [email, setEmail] = useState('') const [password, setPassword] = useState('') return ( ... ) } ...

Następnie musimy zmodyfikować nasze pola <TextInput /> tak, aby pobierały ich wartość z odpowiednich stanów i aktualizowały swój stan, gdy wartość danych wejściowych jest aktualizowana:

 import React, { useState, useCallback } from 'react'; import { View, Text, Button, StyleSheet, TextInput } from 'react-native'; const WelcomeScreen = () => { const [email, setEmail] = useState('') const [password, setPassword] = useState('') const onInputChange = (value, setState) => { setState(value); } return ( <View> ... <View> <TextInput style={styles.inputs} placeholder="Enter your email here.." value={email} onChangeText={(value) => onInputChange(value, setEmail)} /> <TextInput style={styles.inputs} secureTextEntry={true} placeholder="Enter your password here.." value={password} onChangeText={(value) => onInputChange(value, setPassword)} /> ... </View> </View> ) } ...

W powyższym kodzie, oto co zrobiliśmy:

  • Ustaliliśmy, że value każdego z danych wejściowych tekstu wskazuje na ich odpowiedni stan.
  • Dodaliśmy procedurę obsługi onChangeText do naszych danych wejściowych tekstu. To uruchamia się za każdym razem, gdy nowa wartość zostanie wprowadzona lub usunięta z pól wejściowych.
  • Wywołaliśmy naszą funkcję onInputChange , która przyjmuje dwa argumenty:
    • Bieżąca value jest dostarczana przez procedurę obsługi onChangeText .
    • Seter stanu, który ma zostać zaktualizowany (dla pierwszego pola wejściowego przekazujemy setEmail a dla drugiego setPassword .
    • Na koniec piszemy naszą funkcję onInputChange , a nasza funkcja robi tylko jedną rzecz: aktualizuje odpowiednie stany o nową wartość.

Następną rzeczą, nad którą musimy popracować, jest funkcja onUserAuthentication() , która jest wywoływana za każdym razem, gdy zostanie kliknięty przycisk przesyłania formularza.

W idealnym przypadku użytkownik musi już utworzyć konto, a logowanie będzie wymagało pewnego rodzaju logiki zaplecza, aby sprawdzić, czy użytkownik istnieje, a następnie przypisać mu token. W naszym przypadku, ponieważ nie używamy żadnego zaplecza, utworzymy obiekt zawierający prawidłowe dane logowania użytkownika, a następnie uwierzytelnimy użytkownika tylko wtedy, gdy wprowadzone przez niego wartości będą zgodne z naszymi stałymi wartościami z obiektu logowania adresu e- email i password , które będziemy Stwórz.

Oto kod, którego potrzebujemy, aby to zrobić:

 ... const correctAuthenticationDetails = { email: '[email protected]', password: 'password' } const WelcomeScreen = () => { ... // This function gets called when the `AUTHENTICATE` button is clicked const onUserAuthentication = () => { if ( email !== correctAuthenticationDetails.email || password !== correctAuthenticationDetails.password ) { alert('The email or password is incorrect') return } // In here, we will handle what happens if the login details are // correct } ... return ( ... ) } ...

Jedną z pierwszych rzeczy, które zauważysz w powyższym kodzie, jest to, że zdefiniowaliśmy correctAuthenticationDetails (jest to obiekt, który przechowuje prawidłowe dane logowania, jakich oczekujemy od użytkownika) poza składnikiem funkcjonalnym WelcomeScreen() .

Następnie napisaliśmy zawartość funkcji onUserAuthentication() i użyliśmy instrukcji warunkowej, aby sprawdzić, czy email lub password przechowywane w odpowiednich stanach nie zgadza się z tym, który podaliśmy w naszym obiekcie.

Jeśli chcesz zobaczyć, co zrobiliśmy do tej pory, zaimportuj komponent WelcomeScreen do swojego App.js w następujący sposób:

Otwórz plik App.js i umieść go, zastępując cały kod następującym:

 import { StatusBar } from 'expo-status-bar'; import React from 'react'; import { View } from 'react-native'; import WelcomeScreen from './views/screens/preAuthScreens/welcomeScreen'; export default function App() { return ( <View> <StatusBar /> <WelcomeScreen /> </View> ); }

Przyglądając się uważnie powyższemu kodowi, zobaczysz, że zaimportowaliśmy składnik WelcomeScreen , a następnie użyliśmy go w funkcji App() .

Oto jak wygląda wynik naszego WelcomeScreen :

wynik WelcomeScreen
(duży podgląd)

Teraz, gdy skończyliśmy budować komponent WelcomeScreen , przejdźmy dalej i zacznijmy pracować z Context API do zarządzania naszym globalnym stanem.

Dlaczego API kontekstowe?

Korzystając z Context API, nie musimy instalować żadnej dodatkowej biblioteki w ReactJS, konfiguracja jest mniej stresująca i jest jednym z najpopularniejszych sposobów obsługi stanu globalnego w ReactJS. W przypadku lekkiego zarządzania stanem jest to dobry wybór.

Tworzenie naszego kontekstu

Jeśli pamiętasz, wcześniej utworzyliśmy folder kontekstowy i utworzyliśmy w nim podfolder o nazwie authContext .

Przejdźmy teraz do pliku AuthContext.js w folderze authContext i stwórzmy nasz kontekst:

kontekst > authContext > AuthContext.js

 import React, { createContext } from 'react'; const AuthContext = createContext(); export default AuthContext;

AuthContext , który właśnie utworzyliśmy, przechowuje wartość stanu loading i wartości stanu userToken . Obecnie w createContext , które zadeklarowaliśmy w powyższym bloku kodu, nie zainicjowaliśmy tutaj żadnych wartości domyślnych, więc nasz kontekst jest obecnie undefined . Przykładową wartością kontekstu uwierzytelniania może być {loading: false, userToken: 'abcd}

Plik AuthState.js zawiera naszą logikę Context API i ich wartości stanów. Napisane tutaj funkcje można wywoływać z dowolnego miejsca w naszej aplikacji, a gdy aktualizują wartości w stanie, są one również aktualizowane globalnie.

Najpierw wprowadźmy wszystkie importy, których będziemy potrzebować w tym pliku:

kontekst > AuthContext > AuthState.js

 import React, { useState } from 'react'; import AuthContext from './AuthContext'; import AsyncStorage from '@react-native-community/async-storage';

Zaimportowaliśmy hak useState() z ReactJS do przechowywania naszych stanów, zaimportowaliśmy plik AuthContext , który utworzyliśmy powyżej, ponieważ w tym miejscu inicjowany jest nasz pusty kontekst do uwierzytelniania i będziemy musieli go użyć, jak zobaczysz później, w miarę postępów , na koniec importujemy pakiet AsyncStorage (podobnie jak localStorage dla sieci).

AsyncStorage to interfejs API React Native, który umożliwia utrwalanie danych w trybie offline na urządzeniu w aplikacji React Native.

 ... const AuthState = (props) => { const [userToken, setUserToken] = useState(null); const [isLoading, setIsLoading] = useState(true); const onAuthentication = async() => { const USER_TOKEN = "drix1123q2" await AsyncStorage.setItem('user-token', USER_TOKEN); setUserToken(USER_TOKEN); console.warn("user has been authenticated!") } return ( <AuthContext.Provider value={{ onAuthentication, }} > {props.children} </AuthContext.Provider> ) } export default AuthState;

W powyższym bloku kodu oto, co zrobiliśmy:

  • Zadeklarowaliśmy dwa stany dla userToken i isLoading . Stan userToken będzie używany do przechowywania tokena zapisanego w AsyncStorage , natomiast stan isLoading będzie używany do śledzenia stanu ładowania (początkowo jest ustawiony na true ). W dalszej części dowiemy się więcej o wykorzystaniu tych dwóch stanów.

  • Następnie napisaliśmy naszą funkcję onAuthentication() . Ta funkcja jest funkcją async , która jest wywoływana po kliknięciu przycisku logowania w pliku welcomeScreen.jsx . Ta funkcja zostanie wywołana tylko wtedy, gdy podany przez użytkownika adres e-mail i hasło będą pasować do prawidłowego podanego przez nas obiektu szczegółów użytkownika. Zwykle to, co dzieje się podczas uwierzytelniania, polega na tym, że token jest generowany dla użytkownika po uwierzytelnieniu użytkownika na zapleczu przy użyciu pakietu takiego jak JWT, a ten token jest wysyłany do interfejsu użytkownika. Ponieważ nie zajmiemy się tym wszystkim w tym samouczku, utworzyliśmy statyczny token i trzymaliśmy go w zmiennej o nazwie USER_TOKEN .

  • Następnie używamy słowa kluczowego await , aby ustawić nasz token użytkownika na AsyncStorage o nazwie user-token . Instrukcja console.warn() służy tylko do sprawdzenia, czy wszystko poszło dobrze, możesz ją zdjąć, kiedy tylko chcesz.

  • Na koniec przekazujemy naszą funkcję onAuthenticated jako wartość wewnątrz naszego <AuthContext.Provider> , dzięki czemu możemy uzyskać dostęp do funkcji i wywołać ją z dowolnego miejsca w naszej aplikacji.

ekrany > preAuth > welcomeScreen.js

Najpierw zaimportuj useContext z ReactJS i zaimportuj AuthContext z pliku AuthContext.js .

 import React, { useState, useContext } from 'react'; import AuthContext from '../../../context/authContext/AuthContext' ...

Teraz w komponencie funkcjonalnym welcomeScreen() kontekstu, który stworzyliśmy:

 ... const WelcomeScreen = () => { const { onAuthentication } = useContext(AuthContext) const onUserAuthentication = () => { if ( email !== correctAuthenticationDetails.email || password !== correctAuthenticationDetails.password ) { alert('The email or password is incorrect') return } onAuthentication() } return ( ... ) } ...

W powyższym bloku kodu zdestrukturyzowaliśmy funkcję onAuthentication z naszego AuthContext , a następnie wywołaliśmy ją wewnątrz funkcji onUserAuthentication() i usunęliśmy instrukcję console.log() , która była tam wcześniej.

W tej chwili spowoduje to zgłoszenie błędu, ponieważ nie mamy jeszcze dostępu do AuthContext . Aby użyć AuthContext w dowolnym miejscu w aplikacji, musimy umieścić w naszej aplikacji plik najwyższego poziomu z AuthState (w naszym przypadku jest to plik App.js ).

Przejdź do pliku App.js i zastąp tam kod następującym:

 import React from 'react'; import WelcomeScreen from './views/screens/preAuthScreens/welcomeScreen'; import AuthState from './context/authContext/AuthState' export default function App() { return ( <AuthState> <WelcomeScreen /> </AuthState> ); }

Zaszliśmy tak daleko i skończyliśmy z tą sekcją. Zanim przejdziemy do następnej sekcji, w której ustawimy nasz routing, utwórzmy nowy ekran. Ekranem, który zamierzamy utworzyć, będzie plik HomeScreen.js , który ma pojawić się dopiero po pomyślnym uwierzytelnieniu.

Przejdź do: screens > postAuth .

Utwórz nowy plik o nazwie HomeScreen.js . Oto kod pliku HomeScreen.js :

ekrany > postAuth > HomeScreen.js

 import React from 'react'; import { View, Text, Button, StyleSheet } from 'react-native'; const HomeScreen = () => { const onLogout = () => { console.warn("Logout button cliked") } return ( <View style={styles.container}> <Text>Now you're authenticated! Welcome!</Text> <Button title="LOG OUT" onPress={onLogout} /> </View> ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }, }) export default HomeScreen

Na razie przycisk wylogowania ma fikcyjną instrukcję console.log() . Później stworzymy funkcję wylogowania i przekażemy ją na ekran z naszego kontekstu.

Konfigurowanie naszych tras

Musimy utworzyć trzy (3) pliki w naszym folderze nawigacyjnym:

  • postAuthNavigator.js ,
  • preAuthNavigator.js ,
  • AppNavigator.js .

Po utworzeniu tych trzech plików przejdź do utworzonego właśnie pliku preAuthNaviagtor.js i napisz:

nawigacja > preAuthNavigator.js

 import React from "react"; import { createStackNavigator } from "@react-navigation/stack"; import WelcomeScreen from "../screens/preAuthScreens/welcomeScreen"; const PreAuthNavigator = () => { const { Navigator, Screen } = createStackNavigator(); return ( <Navigator initialRouteName="Welcome"> <Screen name="Welcome" component={WelcomeScreen} /> </Navigator> ) } export default PreAuthNavigator;

W powyższym pliku, oto co zrobiliśmy:

  • Zaimportowaliśmy createStackNavigator z @react-navigation/stack , którego używamy do naszej nawigacji stosu. createStackNavigator Umożliwia aplikacji przechodzenie między ekranami, gdzie każdy nowy ekran jest umieszczany na szczycie stosu. Domyślnie nawigator stosów jest skonfigurowany tak, aby mieć znajomy wygląd i działanie systemów iOS i Android: nowe ekrany wsuwają się od prawej w systemie iOS, wygasają od dołu w systemie Android. Kliknij tutaj, jeśli chcesz dowiedzieć się więcej o nawigatorze stosów w React Native.
  • Zdestrukturyzowaliśmy Navigator i Screen z metody createStackNavigator() .
  • W naszej deklaracji zwrotu stworzyliśmy naszą nawigację za pomocą <Navigator/> i stworzyliśmy nasz ekran za pomocą <Screen/> . oznacza to, że gdybyśmy mieli wiele ekranów, do których można uzyskać dostęp przed uwierzytelnieniem, będziemy mieli tutaj wiele <Screen/> reprezentujących je.
  • Na koniec eksportujemy nasz komponent PreAuthNavigator .

Zróbmy podobną rzecz dla pliku postAuthNavigator.js .

nawigacja > postAuthNavigator.js

 import React from "react"; import { createStackNavigator } from "@react-navigation/stack"; import HomeScreen from "../screens/postAuthScreens/HomeScreen"; const PostAuthNavigator = () => { const { Navigator, Screen} = createStackNavigator(); return ( <Navigator initialRouteName="Home"> <Screen name="Home" component={HomeScreen} /> </Navigator> ) } export default PostAuthNavigator;

Jak widać w powyższym kodzie, jedyną różnicą między preAuthNavigator.js a postAuthNavigator.js jest renderowany ekran. Podczas gdy pierwszy z nich zajmuje ekran powitalny , WelcomeScreen zajmuje HomeScreen główny .

Aby stworzyć nasz AppNavigator.js musimy stworzyć kilka rzeczy.

Ponieważ AppNavigator.js to miejsce, w którym będziemy przełączać się i sprawdzać, która trasa będzie dostępna dla użytkownika, potrzebujemy kilku ekranów, aby to działało poprawnie, nakreślmy rzeczy, które musimy najpierw stworzyć:

  1. TransitionScreen.js
    Podczas gdy aplikacja decyduje, którą nawigację zamierza zamontować, chcemy, aby pojawił się ekran przejściowy. Zazwyczaj ekranem przejścia będzie spinner ładowania lub inna niestandardowa animacja wybrana dla aplikacji, ale w naszym przypadku użyjemy podstawowego tagu <Text/> , aby wyświetlić loading… .
  2. checkAuthenticationStatus()
    Ta funkcja jest tym, co będziemy wywoływać, aby sprawdzić stan uwierzytelniania, który określi, który stos nawigacyjny zostanie zamontowany. Stworzymy tę funkcję w naszym kontekście i użyjemy jej w Appnavigator.js .

Teraz chodźmy dalej i stwórzmy nasz plik TransitionScreen.js .

ekrany > TransitionScreen.js

 import React from 'react'; import { Text, View } from 'react-native'; const TransitionScreen = () => { return ( <View> <Text>Loading...</Text> </View> ) } export default TransitionScreen

Nasz ekran przejścia to po prostu prosty ekran, który pokazuje wczytujący się tekst. Zobaczymy, gdzie tego użyć, w dalszej części tego artykułu.

Następnie przejdźmy do naszego AuthState.js i checkAuthenticationStatus() :

context > authContext > AuthState.js

 import React, { useState, useEffect } from 'react'; import AuthContext from './AuthContext'; import AsyncStorage from '@react-native-community/async-storage'; const AuthState = (props) => { const [userToken, setUserToken] = useState(null); const [isLoading, setIsLoading] = useState(true); ... useEffect(() => { checkAuthenticationStatus() }, []) const checkAuthenticationStatus = async () => { try { const returnedToken = await AsyncStorage.getItem('user-toke n'); setUserToken(returnedToken); console.warn('User token set to the state value) } catch(err){ console.warn(`Here's the error that occured while retrievin g token: ${err}`) } setIsLoading(false) } const onAuthentication = async() => { ... } return ( <AuthContext.Provider value={{ onAuthentication, userToken, isLoading, }} > {props.children} </AuthContext.Provider> ) } export default AuthState;

W powyższym bloku kodu napisaliśmy funkcję checkAuthenticationStatus() . W naszej funkcji oto, co robimy:

  • Użyliśmy słowa kluczowego await , aby uzyskać nasz token z AsyncStorage . W przypadku AsyncStorage , jeśli nie podano tokenu, zwraca null . Nasz początkowy stan userToken jest również ustawiony na null .
  • Używamy setUserToken , aby ustawić naszą wartość zwracaną z AsyncStorage jako nasz nowy userToken . Jeśli zwrócona wartość to null , oznacza to, że nasz userToken pozostaje null .
  • Po bloku try{}…catch(){} ustawiamy isLoading na false, ponieważ funkcja sprawdzania statusu uwierzytelniania jest zakończona. Potrzebujemy wartości isLoading , aby wiedzieć, czy nadal powinniśmy wyświetlać TransitionScreen , czy nie. Warto zastanowić się nad ustawieniem błędu, jeśli wystąpi błąd podczas pobierania tokena, abyśmy mogli pokazać użytkownikowi przycisk „Ponów” lub „Spróbuj ponownie” po napotkaniu błędu.
  • Za każdym razem, gdy montuje się AuthState , chcemy sprawdzić status uwierzytelnienia, więc używamy do tego haka useEffect() ReactJS. Wywołujemy naszą checkAuthenticationStatus() wewnątrz useEffect() i ustawiamy wartość isLoading na false po zakończeniu.
  • Na koniec dodajemy nasze stany do naszych wartości <AuthContext.Provider/> , dzięki czemu możemy uzyskać do nich dostęp z dowolnego miejsca w naszej aplikacji objętej interfejsem Context API.

Teraz, gdy mamy już naszą funkcję, czas wrócić do naszego AppNavigator.js i napisać kod do zamontowania konkretnego nawigatora stosu na podstawie stanu uwierzytelnienia:

nawigacja > AppNavigator.js

Najpierw zaimportujemy wszystko, czego potrzebujemy do naszego AppNavigator.js .

 import React, { useEffect, useContext } from "react"; import PreAuthNavigator from "./preAuthNavigator"; import PostAuthNavigator from "./postAuthNavigator"; import { NavigationContainer } from "@react-navigation/native" import { createStackNavigator } from "@react-navigation/stack"; import AuthContext from "../../context/authContext/AuthContext"; import TransitionScreen from "../screens/TransitionScreen";

Teraz, gdy mamy już wszystkie importy, utwórzmy funkcję AppNavigator() .

 ... const AppNavigator = () => { } export default AppNavigator

Następnie przejdziemy do napisania zawartości naszej funkcji AppNavigator() :

 import React, { useState, useEffect, useContext } from "react"; import PreAuthNavigator from "./preAuthNavigator"; import PostAuthNavigator from "./postAuthNavigator"; import { NavigationContainer } from "@react-navigation/native" import { createStackNavigator } from "@react-navigation/stack"; import AuthContext from "../../context/authContext/AuthContext"; import TransitionScreen from "../screens/transition"; const AppNavigator = () => { const { Navigator, Screen } = createStackNavigator(); const authContext = useContext(AuthContext); const { userToken, isLoading } = authContext; if(isLoading) { return <TransitionScreen /> } return ( <NavigationContainer> <Navigator> { userToken == null ? ( <Screen name="PreAuth" component={PreAuthNavigator} options={{ header: () => null }} /> ) : ( <Screen name="PostAuth" component={PostAuthNavigator} options={{ header: () => null }} /> ) } </Navigator> </NavigationContainer> ) } export default AppNavigator

W powyższym bloku kodu, oto zarys tego, co zrobiliśmy:

  • Stworzyliśmy nawigator stosu i zdestrukturyzowaliśmy z niego Navigator i Screen .
  • Zaimportowaliśmy userToken i isLoading z naszego AuthContext
  • Gdy AuthState montuje się, w haczyku useEffecct wywoływana jest funkcja checkAuthenticationStatus() . Używamy instrukcji if , aby sprawdzić, czy isLoading ma wartość true , jeśli jest ona true , zwróconym ekranem jest nasz <TransitionScreen /> , który utworzyliśmy wcześniej, ponieważ funkcja checkAuthenticationStatus() nie jest jeszcze ukończona.
  • Po zakończeniu checkAuthenticationStatus() isLoading jest ustawiane na false i zwracamy nasze główne komponenty nawigacyjne.
  • NavigationContainer został zaimportowany z @react-navigation/native . Jest używany tylko raz w głównym nawigatorze najwyższego poziomu. Zauważ, że nie używamy tego w preAuthNavigator.js lub postAuthNavigator.js.
  • W naszej AppNavigator() nadal tworzymy nawigator stosu. Jeśli userToken z naszego Context API jest null , montujemy PreAuthNavigator , jeśli jego wartość jest inna (co oznacza, że AsyncStorage.getItem() w checkAuthenticationStatus() zwróciła rzeczywistą wartość), montujemy PostAuthNavigator . Nasze renderowanie warunkowe odbywa się za pomocą operatora trójargumentowego.

Teraz skonfigurowaliśmy nasz AppNavigator.js . Następnie musimy przekazać nasz AppNavigator do naszego pliku App.js.

Przekażmy nasz AppNavigator do pliku App.js :

App.js

 ... import AppNavigator from './views/navigation/AppNavigator'; ... return ( <AuthState> <AppNavigator /> </AuthState> );

Zobaczmy teraz, jak obecnie wygląda nasza aplikacja:

Oto, co się dzieje, gdy podczas próby logowania podasz nieprawidłowe dane uwierzytelniające:

Dodawanie funkcji wylogowania

W tym momencie nasz proces uwierzytelniania i wyboru trasy jest zakończony. Jedyne, co pozostało naszej aplikacji, to dodanie funkcji wylogowania.

Przycisk wylogowania znajduje się w pliku HomeScreen.js . Do atrybutu onPress przycisku przekazaliśmy funkcję onLogout() . Na razie w naszej funkcji mamy prostą instrukcję console.log() , ale za chwilę to się zmieni.

Przejdźmy teraz do naszego AuthState.js i napiszmy funkcję wylogowania. Ta funkcja po prostu czyści AsyncStorage , w którym jest zapisany token użytkownika.

context > authContext > AuthState.js

 ... const AuthState = (props) => { ... const userSignout = async() => { await AsyncStorage.removeItem('user-token'); setUserToken(null); } return ( ... ) } export default AuthState;

userSignout() to funkcja asynchroniczna, która usuwa user-token z naszego AsyncStorage .

Teraz musimy wywołać funkcję userSignout() w naszym HomeScreen.js za każdym razem, gdy klikniemy przycisk wylogowania.

Przejdźmy do naszego HomeScreen.js i userSignout() z naszego AuthContext .

ekrany > postAuthScreens > HomeScreen.js

 import React, { useContext } from 'react'; import { View, Text, Button, StyleSheet } from 'react-native'; import AuthContext from '../../../context/authContext/AuthContext' const HomeScreen = () => { const { userSignout } = useContext(AuthContext) const onLogout = () => { userSignout() } return ( <View style={styles.container}> <Text>Now you're authenticated! Welcome!</Text> <Button title="LOG OUT" onPress={onLogout} /> </View> ) } ...

W powyższym bloku kodu zaimportowaliśmy useContext z ReactJS, a następnie zaimportowaliśmy nasz AuthContext. Następnie zdestrukturyzowaliśmy funkcję userSignout z naszego AuthContext i ta userSignout() jest wywoływana w naszej funkcji onLogout() .

Teraz za każdym razem, gdy klikniesz nasz przycisk wylogowania, token użytkownika w naszym AsyncStorage zostanie wyczyszczony.

Voila! cały nasz proces jest zakończony.

Oto, co się dzieje po naciśnięciu przycisku Wstecz po zalogowaniu:

Naciśnięcie przycisku Wstecz po zalogowaniu się do aplikacji.

Oto, co się dzieje po naciśnięciu przycisku Wstecz po wylogowaniu:

Naciśnięcie przycisku Wstecz po wylogowaniu się z aplikacji.

Oto kilka różnych zachowań, które zauważamy podczas używania tego wzorca w naszym przełączaniu stosu nawigacyjnego:

  1. Zauważysz, że nigdzie nie musieliśmy korzystać z navigation.navigate() lub navigation.push() , aby przejść do innej trasy po zalogowaniu. Gdy nasz stan zostanie zaktualizowany tokenem użytkownika, renderowany stos nawigacji jest automatycznie zmieniany.
  2. Naciśnięcie przycisku wstecz na urządzeniu po pomyślnym zalogowaniu nie spowoduje powrotu do strony logowania, zamiast tego całkowicie zamyka aplikację. To zachowanie jest ważne, ponieważ nie chcesz, aby użytkownik mógł wrócić do strony logowania, z wyjątkiem wylogowania się z aplikacji. To samo dotyczy wylogowania — gdy użytkownik się wyloguje, nie może użyć przycisku Wstecz, aby powrócić do ekranu HomeScreen , ale zamiast tego aplikacja się zamyka.

Wniosek

W wielu aplikacjach uwierzytelnianie jest jedną z najważniejszych części, ponieważ potwierdza, że ​​osoba próbująca uzyskać dostęp do chronionych treści ma prawo dostępu do informacji. Nauczenie się, jak zrobić to dobrze, jest ważnym krokiem w budowaniu świetnej, intuicyjnej i łatwej w użyciu/nawigacji aplikacji.

Opierając się na tym kodzie, oto kilka rzeczy, które warto dodać:

  • Walidacja formularza do walidacji pól wejściowych. Sprawdź walidację formularzy React Native za pomocą Formik i Yup.
  • Uwierzytelnianie Firebase do integracji uwierzytelniania z Gmailem, Github, Facebookiem, Twitterem lub niestandardowym interfejsem. Sprawdź React Native Firebase.
  • Koncepcje kodu dla projektantów: Uwierzytelnianie i Autoryzacja.

Oto kilka ważnych zasobów, które znalazłem, które pozwolą ci dowiedzieć się więcej na temat uwierzytelniania, bezpieczeństwa i tego, jak zrobić to dobrze:

Zasoby

  • React Native: Objaśnienie procesu uwierzytelniania użytkownika
  • 10 najlepszych praktyk w zakresie bezpieczeństwa React
  • Metody uwierzytelniania, które mogą zapobiec kolejnym naruszeniom
  • Zobacz kompilację/podgląd na żywo naszej aplikacji tutaj;
  • Zobacz projekt na GitHub.