Обработка монтирования и размонтирования навигационных маршрутов в React Native

Опубликовано: 2022-03-10
Краткий обзор ↬ Часто вам нужны два разных набора навигационных стеков для предварительной и последующей аутентификации пользователя. Обычно, чтобы увидеть больше контента, вы должны пройти аутентификацию каким-либо образом. Давайте посмотрим, как смонтировать и размонтировать стек навигации на основе выполненного условия в React Native.

В этой статье мы рассмотрим монтирование и размонтирование навигационных маршрутов в React Native. Ожидаемое поведение вашего приложения заключается в том, что после выполнения условия аутентификации новый набор маршрутов навигации доступен только вошедшим в систему пользователям, в то время как другие экраны, которые отображались до аутентификации, удаляются и не могут быть возвращены, если только пользователь выходит из приложения.

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

Мы будем работать с Expo для этого проекта, потому что это поможет нам сосредоточиться на текущей проблеме, а не беспокоиться о множестве настроек. Точно такие же шаги, описанные в этой статье, можно выполнить для голого приложения React Native.

Вам нужно немного познакомиться с JavaScript и React Native , чтобы следовать этому руководству. Вот несколько важных вещей, с которыми вы уже должны быть знакомы:

  • Пользовательские компоненты в React Native (как создавать компоненты, получать, передавать и использовать свойства в компоненте). Подробнее.
  • Реагировать на навигацию. Подробнее.
  • Навигатор стека в React Native. Подробнее.
  • Базовые знания компонентов React Native Core ( <View/> , <Text/> и т. д.). Подробнее.
  • AsyncStorage . Подробнее.
  • Контекстный API. Подробнее.

Настройка проекта и базовая аутентификация

Если вы новичок в использовании expo и не знаете, как установить expo, посетите официальную документацию. После завершения установки приступайте к инициализации нового проекта React Native с помощью expo из нашей командной строки:

 expo init navigation-project

Вам будет предложено несколько вариантов выбора базовой настройки:

Базовая настройка проекта React Native
(Большой превью)

В нашем случае давайте выберем первый вариант, чтобы настроить наш проект как пустой документ. Теперь дождитесь завершения установки зависимостей JavaScript.

Как только наше приложение настроено, мы можем изменить наш каталог на наш новый каталог проекта и открыть его в вашем любимом редакторе кода. Нам нужно установить библиотеку, которую мы будем использовать для AsyncStorage и наших навигационных библиотек. Внутри каталога вашей папки в терминале вставьте приведенную выше команду и выберите шаблон ( blank ) для установки зависимостей нашего проекта.

Давайте посмотрим, для чего предназначена каждая из этих зависимостей:

  • @react-native-community/async-storage
    Как и localStorage в Интернете, это React Native API для сохранения данных на устройстве в парах ключ-значение.
  • @react-native-community/masked-view, react-native-screens, react-native-gesture-handle
    Эти зависимости являются основными утилитами, которые используются большинством навигаторов для создания структуры навигации в приложении. (Подробнее читайте в разделе Начало работы с навигацией React Native.)
  • @реагировать-навигация/родной
    Это зависимость для навигации React Native.
  • @реагировать-навигация/стек
    Это зависимость для навигации по стеку в 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

Чтобы запустить приложение, используйте expo start из каталога приложения в вашем терминале. После запуска приложения вы можете использовать приложение expo со своего мобильного телефона для сканирования штрих-кода и просмотра приложения, или, если у вас есть эмулятор Android/симулятор IOS, вы можете открыть приложение через них из инструмента разработчика expo, который открывается в вашем браузере при запуске выставочного приложения. Для примеров изображений в этой статье мы будем использовать Genymotions, чтобы увидеть наш результат. Вот как будет выглядеть наш окончательный результат в Genymotions:

окончательный результат в Genymotions
(Большой превью)

Структуры папок

Давайте создадим нашу структуру папок с самого начала, чтобы нам было легче работать с ней по мере продвижения:

Сначала нам нужны две папки:

  • контекст
    Эта папка будет содержать контекст для всего нашего приложения, так как мы будем работать с Context API для глобального управления состоянием.
  • Просмотры
    Эта папка будет содержать как папку навигации, так и представления для разных экранов.

Идите вперед и создайте две папки в каталоге вашего проекта.

Внутри папки контекста создайте папку с именем authContext и создайте два файла внутри папки authContext :

  • AuthContext.js ,
  • AuthState.js .

Эти файлы понадобятся нам, когда мы начнем работать с Context API.

Теперь переходим в созданную нами папку views и создаем внутри нее еще две папки, а именно:

  • навигация ,
  • экраны .

Теперь, мы еще не закончили, внутри папки screens создайте еще две папки:

  • postAuthScreens ,
  • preAuthScreens .

Если вы правильно выполнили настройку папки, вот как ваша структура папок должна выглядеть на данный момент:

структура папок
(Большой превью)
Еще после прыжка! Продолжить чтение ниже ↓

Создание нашего первого экрана

Теперь давайте создадим наш первый экран и назовем его welcomeScreen.js внутри папки preAuthScreens .

preAuthScreens > welcomeScreen.js

Вот содержимое нашего файла 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

Вот что мы сделали в блоке кода выше:

Во-первых, мы импортировали нужные нам вещи из библиотеки React Native, а именно View , Text , Button , TextInput . Далее мы создали наш функциональный компонент WelcomeScreen .

Вы заметите, что мы импортировали StyleSheet из React Native и использовали ее для определения стилей нашего заголовка, а также нашего <TextInput /> .

Наконец, мы экспортируем компонент WelcomeScreen внизу кода.

Теперь, когда мы закончили с этим, давайте заставим этот компонент функционировать должным образом, используя хук useState для хранения значений входных данных и обновления их состояний каждый раз, когда в полях ввода происходит изменение. Мы также импортируем хук useCallback из React, так как он понадобится нам позже для удержания функции.

Во-первых, пока мы все еще находимся в компоненте WelcomeScreen , нам нужно импортировать useState и useCallback из React.

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

Теперь внутри функционального компонента WelcomeScreen создадим два состояния для электронной почты и пароля соответственно:

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

Затем нам нужно изменить наши поля <TextInput /> , чтобы они получали их значение из соответствующих состояний и обновляли свое состояние при обновлении значения ввода:

 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> ) } ...

В приведенном выше коде вот что мы сделали:

  • Мы сделали value каждого из текстовых входов, чтобы указать на их соответствующие состояния.
  • Мы добавили обработчик onChangeText в наши текстовые поля. Это запускается каждый раз, когда новое значение вводится или удаляется из полей ввода.
  • Мы вызвали нашу функцию onInputChange , которая принимает два аргумента:
    • Текущее value предоставляется обработчиком onChangeText .
    • Сеттер состояния, которое должно быть обновлено (для первого поля ввода мы передаем setEmail а для второго — setPassword .
    • Наконец, мы пишем нашу функцию onInputChange , и наша функция делает только одну вещь: обновляет соответствующие состояния с новым значением.

Следующее, над чем нам нужно поработать, это onUserAuthentication() , которая вызывается всякий раз, когда нажимается кнопка отправки формы.

В идеале пользователь должен уже создать учетную запись, и вход в систему будет включать некоторую внутреннюю логику, чтобы проверить, что пользователь существует, а затем назначить пользователю токен. В нашем случае, поскольку мы не используем какой-либо бэкэнд, мы создадим объект, содержащий правильные данные для входа пользователя, а затем аутентифицируем пользователя только тогда, когда значения, которые они вводят, совпадают с нашими фиксированными значениями из объекта входа в систему email и password , которые мы будем Создайте.

Вот код, который нам нужен для этого:

 ... 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 ( ... ) } ...

Одна из первых вещей, которую вы заметите в приведенном выше коде, это то, что мы определили correctAuthenticationDetails (объект, содержащий правильные данные для входа в систему, которые мы ожидаем от пользователя) вне функционального компонента WelcomeScreen() .

Затем мы написали содержимое функции onUserAuthentication() и использовали условный оператор, чтобы проверить, не совпадают ли email или password , хранящиеся в соответствующих состояниях, с теми, которые мы указали в нашем объекте.

Если вы хотите увидеть, что мы уже сделали, импортируйте компонент WelcomeScreen в свой App.js следующим образом:

Откройте файл App.js и замените весь код следующим:

 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> ); }

Внимательно изучив приведенный выше код, вы увидите, что мы импортировали компонент WelcomeScreen , а затем использовали его в функции App() .

Вот как выглядит результат нашего WelcomeScreen :

результат приветственного экрана
(Большой превью)

Теперь, когда мы закончили создание компонента WelcomeScreen , давайте двигаться дальше и начать работать с Context API для управления нашим глобальным состоянием.

Почему контекстный API?

Используя Context API, нам не нужно устанавливать какую-либо дополнительную библиотеку в ReactJS, его настройка менее утомительна, и это один из самых популярных способов обработки глобального состояния в ReactJS. Для упрощенного управления состоянием это хороший выбор.

Создание нашего контекста

Если вы помните, ранее мы создали контекстную папку и создали в ней подпапку с именем authContext .

Теперь давайте перейдем к файлу AuthContext.js в папке authContext и создадим наш контекст:

контекст > authContext > AuthContext.js

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

Только что созданный AuthContext содержит значение состояния loading и значения состояния userToken . В настоящее время в createContext , который мы объявили в блоке кода выше, мы не инициализировали здесь никаких значений по умолчанию, поэтому наш контекст в настоящее время undefined . Примером значения контекста аутентификации может быть {loading: false, userToken: 'abcd}

Файл AuthState.js содержит нашу логику Context API и их значения состояния. Функции, написанные здесь, можно вызывать из любого места в нашем приложении, и когда они обновляют значения в состоянии, оно также обновляется глобально.

Во-первых, давайте внесем в этот файл все импорты, которые нам понадобятся:

контекст > AuthContext > AuthState.js

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

Мы импортировали useState() из ReactJS для хранения наших состояний, мы импортировали файл AuthContext , который мы создали выше, потому что именно здесь инициализируется наш пустой контекст для аутентификации, и нам нужно будет использовать его, как вы увидите позже, пока мы продвигаемся. , наконец, мы импортируем пакет AsyncStorage (аналогичный localStorage для Интернета).

AsyncStorage — это API React Native, который позволяет сохранять данные в автономном режиме на устройстве в приложении 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;

В блоке кода выше вот что мы сделали:

  • Мы объявили два состояния для userToken и isLoading . Состояние userToken будет использоваться для хранения токена, сохраненного в AsyncStorage , а состояние isLoading — для отслеживания состояния загрузки (изначально установлено значение true ). Мы узнаем больше об использовании этих двух состояний по ходу дела.

  • Затем мы написали нашу onAuthentication() . Эта функция представляет собой async функцию, которая вызывается при нажатии кнопки входа в файл welcomeScreen.jsx . Эта функция будет вызываться только в том случае, если адрес электронной почты и пароль, предоставленные пользователем, совпадают с правильным объектом сведений о пользователе, который мы предоставили. Обычно во время аутентификации происходит токен, который создается для пользователя после того, как пользователь аутентифицируется на бэкэнде с помощью пакета, такого как JWT, и этот токен отправляется во внешний интерфейс. Поскольку мы не будем вдаваться во все это в этом руководстве, мы создали статический токен и сохранили его в переменной с именем USER_TOKEN .

  • Затем мы используем ключевое слово await , чтобы установить для нашего пользовательского токена значение AsyncStorage с именем user-token . Оператор console.warn() просто используется для проверки того, что все прошло правильно, вы можете отключить его, когда захотите.

  • Наконец, мы передаем нашу функцию onAuthenticated как значение внутри нашего <AuthContext.Provider> , чтобы мы могли получить доступ и вызвать функцию из любого места в нашем приложении.

экраны > preAuth > welcomeScreen.js

Сначала импортируйте useContext из ReactJS и импортируйте AuthContext из файла AuthContext.js .

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

Теперь внутри функционального компонента welcomeScreen() воспользуемся созданным нами контекстом:

 ... 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 ( ... ) } ...

В приведенном выше блоке кода мы деструктурировали функцию onAuthentication из нашего AuthContext а затем вызвали ее внутри нашей onUserAuthentication() и удалили оператор console.log() , который был там до сих пор.

Прямо сейчас это вызовет ошибку, потому что у нас еще нет доступа к AuthContext . Чтобы использовать AuthContext в любом месте вашего приложения, нам нужно обернуть файл верхнего уровня в нашем приложении с помощью AuthState (в нашем случае это файл App.js ).

Перейдите в файл App.js и замените там код на этот:

 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> ); }

Мы зашли так далеко, и мы закончили с этим разделом. Прежде чем мы перейдем к следующему разделу, где мы настроим нашу маршрутизацию, давайте создадим новый экран. Экран, который мы собираемся создать, будет файлом HomeScreen.js , который должен появиться только после успешной аутентификации.

Перейдите по ссылке: экраны > postAuth .

Создайте новый файл с именем HomeScreen.js . Вот код файла HomeScreen.js :

экраны > 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

На данный момент кнопка выхода имеет фиктивный оператор console.log() . Позже мы создадим функцию выхода из системы и передадим ее на экран из нашего контекста.

Настройка наших маршрутов

Нам нужно создать три (3) файла в нашей навигационной папке:

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

Создав эти три файла, перейдите к только что созданному файлу preAuthNaviagtor.js и напишите следующее:

навигация > 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;

В файле выше вот что мы сделали:

  • Мы импортировали createStackNavigator из @react-navigation/stack который используем для навигации по стеку. createStackNavigator Предоставляет вашему приложению способ перехода между экранами, где каждый новый экран помещается поверх стека. По умолчанию навигатор стека настроен на знакомый внешний вид iOS и Android: новые экраны появляются справа на iOS и исчезают снизу на Android. Нажмите здесь, если хотите узнать больше о навигаторе стека в React Native.
  • Мы деструктурировали Navigator и Screen из createStackNavigator() .
  • В нашем операторе return мы создали нашу навигацию с помощью <Navigator/> и создали наш экран с помощью <Screen/> . это означает, что если бы у нас было несколько экранов, к которым можно получить доступ до аутентификации, у нас будет несколько тегов <Screen/> , представляющих их здесь.
  • Наконец, мы экспортируем наш компонент PreAuthNavigator .

Давайте сделаем то же самое для файла postAuthNavigator.js .

навигация > 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;

Как видно из приведенного выше кода, единственная разница между preAuthNavigator.js и postAuthNavigator.js заключается в отображаемом экране. В то время как первый занимает WelcomeScreen , postAuthNavigator.js принимает HomeScreen .

Чтобы создать наш AppNavigator.js , нам нужно создать несколько вещей.

Поскольку AppNavigator.js — это место, где мы будем переключаться и проверять, какой маршрут будет доступен для доступа пользователя, нам нужно несколько экранов, чтобы это работало правильно, давайте наметим то, что нам нужно создать в первую очередь:

  1. TransitionScreen.js
    Пока приложение решает, какую навигацию оно собирается монтировать, мы хотим, чтобы отображался экран перехода. Как правило, экран перехода представляет собой счетчик загрузки или любую другую пользовательскую анимацию, выбранную для приложения, но в нашем случае мы будем использовать базовый <Text/> для отображения loading… .
  2. checkAuthenticationStatus()
    Эту функцию мы будем вызывать для проверки статуса аутентификации, который определит, какой навигационный стек будет смонтирован. Мы создадим эту функцию в нашем контексте и будем использовать ее в Appnavigator.js .

Теперь давайте продолжим и создадим наш файл TransitionScreen.js .

экраны > TransitionScreen.js

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

Наш экран перехода — это просто простой экран, который показывает загрузку текста. Мы увидим, где это использовать, в этой статье.

Далее давайте перейдем к нашему AuthState.js и напишем наш checkAuthenticationStatus() :

контекст > 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;

В приведенном выше блоке кода мы написали функцию checkAuthenticationStatus() . В нашей функции вот что мы делаем:

  • Мы использовали ключевое слово await , чтобы получить наш токен из AsyncStorage . С AsyncStorage , если маркер не предоставлен, он возвращает null . Наше начальное состояние userToken также имеет значение null .
  • Мы используем setUserToken , чтобы установить возвращаемое из AsyncStorage значение в качестве нового userToken . Если возвращаемое значение равно null , это означает, что наш userToken остается null .
  • После блока try{}…catch(){} мы устанавливаем для isLoading значение false, поскольку функция проверки статуса аутентификации завершена. Нам понадобится значение isLoading , чтобы узнать, должны ли мы по-прежнему отображать TransitionScreen или нет. Стоит подумать об установке ошибки, если при получении токена возникает ошибка, чтобы мы могли показать пользователю кнопку «Повторить попытку» или «Попробовать еще раз» при обнаружении ошибки.
  • Всякий раз, когда AuthState монтируется, мы хотим проверить статус аутентификации, поэтому для этого мы используем useEffect() ReactJS. Мы вызываем нашу checkAuthenticationStatus() внутри useEffect() и устанавливаем значение isLoading в false , когда это делается.
  • Наконец, мы добавляем наши состояния к нашим значениям <AuthContext.Provider/> , чтобы мы могли получить к ним доступ из любого места в нашем приложении, охваченном Context API.

Теперь, когда у нас есть наша функция, пришло время вернуться к нашему AppNavigator.js и написать код для монтирования определенного навигатора стека на основе статуса аутентификации:

навигация > AppNavigator.js

Во-первых, мы импортируем все, что нам нужно для нашего 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";

Теперь, когда у нас есть все импортируемые данные, давайте создадим AppNavigator() .

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

Теперь мы приступим к написанию содержимого нашей 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

В приведенном выше блоке кода показано, что мы сделали:

  • Мы создали навигатор стека и деструктурировали из него Navigator и Screen .
  • Мы импортировали userToken и isLoading из нашего AuthContext
  • Когда AuthState монтируется, в хуке useEffecct вызывается useEffecct checkAuthenticationStatus() . Мы используем оператор if , чтобы проверить, является ли isLoading true , если это true , то экран, который мы возвращаем, является нашим <TransitionScreen /> , который мы создали ранее, потому что checkAuthenticationStatus() еще не завершена.
  • Как только наш checkAuthenticationStatus() завершен, для isLoading устанавливается значение false , и мы возвращаем наши основные компоненты навигации.
  • NavigationContainer был импортирован из @react-navigation/native . Он используется только один раз в основном навигаторе верхнего уровня. Обратите внимание, что мы не используем это в preAuthNavigator.js или postAuthNavigator.js.
  • В нашем AppNavigator() мы по-прежнему создаем навигатор стека. Если userToken полученный из нашего Context API, имеет значение null , мы монтируем PreAuthNavigator , если его значение другое (имеется в виду, что AsyncStorage.getItem() в checkAuthenticationStatus() возвращает фактическое значение), то мы монтируем PostAuthNavigator . Наш условный рендеринг выполняется с помощью тернарного оператора.

Теперь мы настроили наш AppNavigator.js . Далее нам нужно передать наш AppNavigator в наш файл App.js.

Давайте передадим наш AppNavigator в файл App.js :

App.js

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

Давайте теперь посмотрим, как выглядит наше приложение на данный момент:

Вот что происходит, когда вы указываете неверные учетные данные при попытке входа в систему:

Добавление функции выхода из системы

На этом наш процесс аутентификации и выбора маршрута завершен. Единственное, что осталось для нашего приложения, это добавить функцию выхода из системы.

Кнопка выхода находится в файле HomeScreen.js . Мы передали функцию onLogout() в атрибут onPress кнопки. На данный момент у нас есть простой оператор console.log() в нашей функции, но через некоторое время это изменится.

Теперь давайте перейдем к нашему AuthState.js и напишем функцию выхода из системы. Эта функция просто очищает AsyncStorage где сохранен токен пользователя.

контекст > authContext > AuthState.js

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

userSignout() — это асинхронная функция, которая удаляет user-token из нашего AsyncStorage .

Теперь нам нужно вызывать userSignout() в нашем HomeScreen.js каждый раз, когда нажимается кнопка выхода.

Давайте перейдем к нашему HomeScreen.js и используем userSignout() из нашего AuthContext .

экраны > 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> ) } ...

В приведенном выше блоке кода мы импортировали хук useContext из ReactJS, а затем импортировали наш AuthContext. Затем мы деструктурировали функцию userSignout из нашего AuthContext и эта userSignout() вызывается в нашей onLogout() .

Теперь всякий раз, когда нажимается наша кнопка выхода, токен пользователя в нашем AsyncStorage очищается.

Вуаля! весь наш процесс завершен.

Вот что происходит, когда вы нажимаете кнопку «Назад» после входа в систему:

Нажатие кнопки «Назад» после входа в приложение.

Вот что происходит, когда вы нажимаете кнопку «Назад» после выхода из системы:

Нажатие кнопки «Назад» после выхода из приложения.

Вот некоторые особенности поведения, которые мы замечаем при использовании этого шаблона при переключении стека навигации:

  1. Вы заметите, что нам нигде не нужно было использовать navigation.navigate() или navigation.push() для перехода на другой маршрут после входа в систему. Как только наше состояние обновляется токеном пользователя, отображаемый стек навигации автоматически изменяется.
  2. Нажатие кнопки «Назад» на вашем устройстве после успешного входа в систему не вернет вас на страницу входа, а полностью закроет приложение. Такое поведение важно, потому что вы не хотите, чтобы пользователь мог вернуться на страницу входа, если он не выйдет из приложения. То же самое относится и к выходу из системы — после выхода пользователь не может использовать кнопку «Назад», чтобы вернуться на экран HomeScreen , вместо этого приложение закрывается.

Заключение

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

Основываясь на этом коде, вот несколько вещей, которые вы можете добавить:

  • Проверка формы для проверки полей ввода. Ознакомьтесь с проверкой формы React Native с помощью Formik и Yup.
  • Аутентификация Firebase для интеграции аутентификации с Gmail, Github, Facebook, Twitter или вашим пользовательским интерфейсом. Проверьте React Native Firebase.
  • Концепции кода для дизайнеров: аутентификация и авторизация.

Вот также некоторые важные ресурсы, которые я нашел, которые расскажут вам больше об аутентификации, безопасности и о том, как это сделать правильно:

Ресурсы

  • React Native: объяснение процесса аутентификации пользователя
  • 10 лучших практик безопасности React
  • Методы аутентификации, которые могут предотвратить следующее нарушение
  • Посмотреть живую сборку/превью нашего приложения можно здесь;
  • Посмотреть проект на GitHub.