Manejo del montaje y desmontaje de rutas de navegación en React Native

Publicado: 2022-03-10
Resumen rápido ↬ A menudo, necesita dos conjuntos diferentes de pilas de navegación para la autenticación previa y posterior al usuario. Por lo general, para ver más contenido, debe estar autenticado de alguna manera. Veamos cómo montar y desmontar la pila de navegación en función de una condición cumplida en React Native.

En este artículo, vamos a recorrer el montaje y desmontaje de rutas de navegación en React Native. Un comportamiento esperado de su aplicación es que una vez que se cumple la condición de autenticación, un nuevo conjunto de rutas de navegación está disponible solo para los usuarios registrados, mientras que las otras pantallas que se mostraban antes de la autenticación se eliminan y no se pueden volver a menos que el el usuario cierra la sesión de la aplicación.

Por seguridad en su aplicación, las rutas protegidas le brindan una forma de mostrar solo cierta información/contenido en su aplicación a usuarios específicos, mientras restringe el acceso de personas no autorizadas.

Trabajaremos con Expo para este proyecto porque nos ayudará a concentrarnos en el problema en cuestión en lugar de preocuparnos por muchas configuraciones. Se pueden seguir exactamente los mismos pasos en este artículo para una aplicación React Native simple.

Necesita cierta familiaridad con JavaScript y React Native para continuar con este tutorial. Aquí hay algunas cosas importantes con las que ya debería estar familiarizado:

  • Componentes personalizados en React Native (cómo crear componentes, recibir, pasar y usar accesorios en un componente). Leer más.
  • Reaccionar Navegación. Leer más.
  • Navegador de pila en React Native. Leer más.
  • Conocimiento básico de los componentes de React Native Core ( <View/> , <Text/> , etc.). Leer más.
  • Reaccionar AsyncStorage nativo. Leer más.
  • API de contexto. Leer más.

Configuración del proyecto y autenticación base

Si es nuevo en el uso de expo y no sabe cómo instalarlo, visite la documentación oficial. Una vez que se complete la instalación, continúe para inicializar un nuevo proyecto React Native con expo desde nuestro símbolo del sistema:

 expo init navigation-project

Se le presentarán algunas opciones para elegir cómo desea que sea la configuración base:

Configuración base del proyecto React Native
(Vista previa grande)

En nuestro caso, seleccionemos la primera opción para configurar nuestro proyecto como un documento en blanco. Ahora, espere hasta que se complete la instalación de las dependencias de JavaScript.

Una vez que nuestra aplicación está configurada, podemos cambiar nuestro directorio a nuestro nuevo directorio de proyectos y abrirlo en su editor de código favorito. Necesitamos instalar la biblioteca que usaremos para AsyncStorage y nuestras bibliotecas de navegación. Dentro de su directorio de carpetas en su terminal, pegue el comando anterior y elija una plantilla (funcionaría blank ) para instalar las dependencias de nuestro proyecto.

Veamos para qué sirve cada una de estas dependencias:

  • @react-native-community/async-almacenamiento
    Al igual que localStorage en la web, es una API React Native para almacenar datos en un dispositivo en pares clave-valor.
  • @react-native-community/masked-view, react-native-screens, react-native-gesture-handle
    Estas dependencias son utilidades principales que utilizan la mayoría de los navegadores para crear la estructura de navegación en la aplicación. (Lea más en Primeros pasos con la navegación de React Native).
  • @reaccionar-navegación/nativo
    Esta es la dependencia para la navegación de React Native.
  • @reaccionar-navegación/pila
    Esta es la dependencia para la navegación de pila en 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

Para iniciar la aplicación, use expo start desde el directorio de la aplicación en su terminal. Una vez que se inicia la aplicación, puede usar la aplicación expo desde su teléfono móvil para escanear el código de barras y ver la aplicación, o si tiene un emulador de Android/simulador IOS, puede abrir la aplicación a través de ellos desde la herramienta de desarrollo expo que se abre en su navegador cuando inicia una aplicación de exposición. Para los ejemplos de imágenes en este artículo, usaremos Genymotions para ver nuestro resultado. Así es como se verá nuestro resultado final en Genymotions:

resultado final en Genymotions
(Vista previa grande)

Estructuras de carpetas

Vamos a crear nuestra estructura de carpetas desde el principio para que nos sea más fácil trabajar con ella a medida que avanzamos:

Primero necesitamos dos carpetas:

  • contexto
    Esta carpeta contendrá el contexto de toda nuestra aplicación, ya que trabajaremos con la API de contexto para la gestión del estado global.
  • puntos de vista
    Esta carpeta contendrá tanto la carpeta de navegación como las vistas de las diferentes pantallas.

Continúe y cree las dos carpetas en el directorio de su proyecto.

Dentro de la carpeta de contexto, cree una carpeta llamada authContext y cree dos archivos dentro de la carpeta authContext :

  • AuthContext.js ,
  • AuthState.js .

Necesitaremos estos archivos cuando comencemos a trabajar con Context API.

Ahora vaya a la carpeta de vistas que creamos y cree dos carpetas más dentro de ella, a saber:

  • navegación ,
  • pantallas

Ahora, aún no hemos terminado, dentro de la carpeta de pantallas , cree estas dos carpetas más:

  • pantallas postauth ,
  • PreAuthScreens .

Si siguió la configuración de la carpeta correctamente, así es como debería verse su estructura de carpetas en este momento:

estructura de carpetas
(Vista previa grande)
¡Más después del salto! Continúe leyendo a continuación ↓

Creando nuestra primera pantalla

Ahora vamos a crear nuestra primera pantalla y llamarla welcomeScreen.js dentro de la carpeta preAuthScreens .

preAuthScreens > pantalla de bienvenida.js

Este es el contenido de nuestro archivo 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

Esto es lo que hicimos en el bloque de código anterior:

Primero, importamos las cosas que necesitamos de la biblioteca React Native, a saber, View , Text , Button , TextInput . A continuación, creamos nuestro componente funcional WelcomeScreen .

Notarás que StyleSheet de React Native y lo usamos para definir estilos para nuestro encabezado y también nuestro <TextInput /> .

Por último, exportamos el componente WelcomeScreen en la parte inferior del código.

Ahora que hemos terminado con esto, hagamos que este componente funcione como se esperaba usando el gancho useState para almacenar los valores de las entradas y actualizar sus estados cada vez que ocurra un cambio en los campos de entrada. También importaremos el useCallback de React, ya que lo necesitaremos más adelante para mantener una función.

Primero, mientras todavía estamos en el componente WelcomeScreen , necesitamos importar useState y useCallback desde React.

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

Ahora, dentro del componente funcional WelcomeScreen , creemos los dos estados para el correo electrónico y la contraseña, respectivamente:

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

A continuación, debemos modificar nuestros campos <TextInput /> para que obtengan su valor de sus respectivos estados y actualicen su estado cuando se actualice el valor de la entrada:

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

En el código anterior, esto es lo que hicimos:

  • Hicimos que el value de cada una de las entradas de texto apunte a sus respectivos estados.
  • Agregamos el controlador onChangeText a nuestras entradas de texto. Esto se activa cada vez que se ingresa o elimina un nuevo valor de los campos de entrada.
  • Llamamos a nuestra función onInputChange que acepta dos argumentos:
    • El value actual lo proporciona el controlador onChangeText .
    • El setter del estado que debe actualizarse (para el primer campo de entrada, pasamos setEmail y el segundo, setPassword .
    • Finalmente, escribimos nuestra función onInputChange , y nuestra función solo hace una cosa: actualiza los estados respectivos con el nuevo valor.

Lo siguiente en lo que debemos trabajar es en la función onUserAuthentication() que se llama cada vez que se hace clic en el botón para enviar el formulario.

Idealmente, el usuario ya debe haber creado una cuenta y el inicio de sesión implicará algún tipo de lógica de back-end para verificar que el usuario existe y luego asignarle un token. En nuestro caso, dado que no estamos utilizando ningún backend, crearemos un objeto que contenga el detalle de inicio de sesión de usuario correcto y luego solo autenticaremos a un usuario cuando los valores que ingresen coincidan con nuestros valores fijos del objeto de inicio de sesión de email y password que usaremos. crear.

Aquí está el código que necesitamos para hacer esto:

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

Una de las primeras cosas que notará en el código anterior es que definimos un detalle de correctAuthenticationDetails (que es un objeto que contiene los detalles de inicio de sesión correctos que esperamos que proporcione un usuario) fuera del componente funcional WelcomeScreen() .

A continuación, escribimos el contenido de la función onUserAuthentication() y usamos una declaración condicional para verificar si el email o la password que se encuentran en los estados respectivos no coinciden con los que proporcionamos en nuestro objeto.

Si desea ver lo que hemos hecho hasta ahora, importe el componente WelcomeScreen en su App.js de esta manera:

Abra el archivo App.js y coloque esto, reemplace todo el código con esto:

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

Mirando de cerca el código anterior, verá que lo que hicimos fue importar el componente WelcomeScreen y luego lo usamos en la función App() .

Así es como se ve el resultado de nuestra WelcomeScreen de bienvenida:

el resultado de WelcomeScreen
(Vista previa grande)

Ahora que hemos terminado de crear el componente WelcomeScreen , avancemos y comencemos a trabajar con la API de contexto para administrar nuestro estado global.

¿Por qué API de contexto?

Con la API de contexto, no necesitamos instalar ninguna biblioteca adicional en ReactJS, es menos estresante de configurar y es una de las formas más populares de manejar el estado global en ReactJS. Para la gestión de estados ligeros, es una buena opción.

Creando nuestro contexto

Si recuerda, creamos una carpeta de contexto anteriormente y creamos una subcarpeta dentro de ella llamada authContext .

Ahora naveguemos al archivo AuthContext.js en la carpeta authContext y creemos nuestro contexto:

contexto > authContext > AuthContext.js

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

El AuthContext que acabamos de crear contiene el valor del estado de loading y los valores del estado del userToken de usuario. Actualmente, en el createContext que declaramos en el bloque de código anterior, no inicializamos ningún valor predeterminado aquí, por lo que nuestro contexto actualmente no está undefined . Un valor de ejemplo del contexto de autenticación podría ser {loading: false, userToken: 'abcd}

El archivo AuthState.js contiene nuestra lógica API de contexto y sus valores de estado. Las funciones escritas aquí se pueden llamar desde cualquier lugar de nuestra aplicación y cuando actualizan los valores en el estado, también se actualizan globalmente.

Primero, traigamos todas las importaciones que necesitaremos en este archivo:

contexto > AuthContext > AuthState.js

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

Importamos el useState() de ReactJS para mantener nuestros estados, importamos el archivo AuthContext que creamos anteriormente porque aquí es donde se inicializa nuestro contexto vacío para la autenticación y necesitaremos usarlo como verá más adelante mientras avanzamos. , finalmente importamos el paquete AsyncStorage (similar a localStorage para la web).

AsyncStorage es una API de React Native que le permite almacenar datos sin conexión en el dispositivo en una aplicación de 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;

En el bloque de código de arriba, esto es lo que hicimos:

  • Declaramos dos estados para userToken e isLoading . El estado userToken se usará para almacenar el token guardado en AsyncStorage , mientras que el estado isLoading se usará para rastrear el estado de carga (inicialmente se establece en true ). Descubriremos más sobre el uso de estos dos estados a medida que avancemos.

  • A continuación, escribimos nuestra función onAuthentication() . Esta función es una función async que se llama cuando se hace clic en el botón de inicio de sesión desde el archivo welcomeScreen.jsx . Esta función solo se llamará si el correo electrónico y la contraseña que el usuario proporcionó coinciden con el objeto de detalles de usuario correcto que proporcionamos. Por lo general, lo que sucede durante la autenticación es que se genera un token para el usuario después de que el usuario se autentica en el backend mediante un paquete como JWT, y este token se envía al frontend. Como no vamos a entrar en todo eso en este tutorial, creamos un token estático y lo guardamos en una variable llamada USER_TOKEN .

  • A continuación, usamos la palabra clave await para establecer nuestro token de usuario en AsyncStorage con el nombre user-token . La instrucción console.warn() solo se usa para verificar que todo salió bien, puede quitarla cuando lo desee.

  • Finalmente, pasamos nuestra función onAuthenticated como un valor dentro de nuestro <AuthContext.Provider> para que podamos acceder y llamar a la función desde cualquier lugar de nuestra aplicación.

pantallas > autenticación previa > pantalla de bienvenida.js

Primero, importe useContext desde ReactJS e importe el AuthContext desde el archivo AuthContext.js .

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

Ahora, dentro del componente funcional welcomeScreen() , usemos el contexto que hemos creado:

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

En el bloque de código anterior, desestructuramos la función onAuthentication de nuestro AuthContext y luego la llamamos dentro de nuestra función onUserAuthentication() y eliminamos la instrucción console.log() que estaba allí antes.

En este momento, esto arrojará un error porque aún no tenemos acceso al AuthContext . Para usar AuthContext en cualquier parte de su aplicación, debemos envolver el archivo de nivel superior en nuestra aplicación con AuthState (en nuestro caso, es el archivo App.js ).

Vaya al archivo App.js y reemplace el código allí con esto:

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

Hemos llegado hasta aquí y hemos terminado con esta sección. Antes de pasar a la siguiente sección donde configuramos nuestro enrutamiento, creemos una nueva pantalla. La pantalla que estamos a punto de crear será el archivo HomeScreen.js que se supone que aparecerá solo después de una autenticación exitosa.

Vaya a: pantallas > postAuth .

Cree un nuevo archivo llamado HomeScreen.js . Aquí está el código para el archivo HomeScreen.js :

pantallas > 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

Por ahora, el botón de cierre de sesión tiene una instrucción console.log() ficticia. Más adelante, crearemos la funcionalidad de cierre de sesión y la pasaremos a la pantalla desde nuestro contexto.

Configuración de nuestras rutas

Necesitamos crear tres (3) archivos dentro de nuestra carpeta de navegación:

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

Una vez que haya creado estos tres archivos, navegue hasta el archivo preAuthNaviagtor.js que acaba de crear y escriba esto:

navegación > 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;

En el archivo de arriba, esto es lo que hicimos:

  • Importamos el createStackNavigator de @react-navigation/stack que estamos usando para nuestra navegación de pila. El createStackNavigator proporciona una forma para que su aplicación haga la transición entre pantallas donde cada nueva pantalla se coloca encima de una pila. De forma predeterminada, el navegador de pila está configurado para tener el aspecto familiar de iOS y Android: las nuevas pantallas se deslizan desde la derecha en iOS, se desvanecen desde la parte inferior en Android. Haga clic aquí si desea obtener más información sobre el navegador de pila en React Native.
  • Desestructuramos Navigator y Screen desde createStackNavigator() .
  • En nuestra declaración de devolución, creamos nuestra navegación con <Navigator/> y creamos nuestra pantalla con <Screen/> . esto significa que si tuviéramos múltiples pantallas a las que se puede acceder antes de la autenticación, tendremos múltiples etiquetas <Screen/> aquí representándolas.
  • Finalmente, exportamos nuestro componente PreAuthNavigator .

Hagamos algo similar para el archivo postAuthNavigator.js .

navegación > 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;

Como vemos en el código anterior, la única diferencia entre preAuthNavigator.js y postAuthNavigator.js es la pantalla que se representa. Mientras que el primero toma la pantalla de bienvenida, el WelcomeScreen toma la pantalla de HomeScreen .

Para crear nuestro AppNavigator.js necesitamos crear algunas cosas.

Dado que AppNavigator.js es donde cambiaremos y verificaremos qué ruta estará disponible para el acceso del usuario, necesitamos varias pantallas para que esto funcione correctamente, describamos las cosas que debemos crear primero:

  1. TransitionScreen.js
    Mientras la aplicación decide qué navegación va a montar, queremos que aparezca una pantalla de transición. Por lo general, la pantalla de transición será una rueda giratoria de carga o cualquier otra animación personalizada elegida para la aplicación, pero en nuestro caso, usaremos una etiqueta básica <Text/> para mostrar la loading… .
  2. checkAuthenticationStatus()
    Esta función es la que llamaremos para verificar el estado de autenticación que determinará qué pila de navegación se montará. Crearemos esta función en nuestro contexto y la usaremos en Appnavigator.js .

Ahora, avancemos y creemos nuestro archivo TransitionScreen.js .

pantallas > TransitionScreen.js

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

Nuestra pantalla de transición es solo una pantalla simple que muestra el texto de carga. Veremos dónde usar esto a medida que avancemos en este artículo.

A continuación, vayamos a nuestro AuthState.js y escribamos nuestro checkAuthenticationStatus() :

contexto > 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;

En el bloque de código anterior, escribimos la función checkAuthenticationStatus() . En nuestra función, esto es lo que estamos haciendo:

  • Usamos la palabra clave await para obtener nuestro token de AsyncStorage . Con AsyncStorage , si no se proporciona ningún token, devuelve null . Nuestro estado inicial userToken de usuario también se establece en null .
  • Usamos setUserToken para establecer nuestro valor devuelto por AsyncStorage como nuestro nuevo userToken de usuario. Si el valor devuelto es null , significa que nuestro userToken sigue siendo null .
  • Después del bloque try{}…catch(){} , establecemos isLoading en falso porque la función para verificar el estado de autenticación está completa. Necesitaremos el valor de isLoading para saber si aún deberíamos mostrar la pantalla de TransitionScreen o no. Vale la pena considerar configurar un error si hay un error al recuperar el token para que podamos mostrarle al usuario un botón "Reintentar" o "Intentar de nuevo" cuando se encuentre el error.
  • Cada vez que se monta AuthState , queremos verificar el estado de autenticación, por lo que usamos el useEffect() ReactJS para hacer esto. Llamamos a nuestra función checkAuthenticationStatus() dentro del useEffect() y establecemos el valor de isLoading en false cuando haya terminado.
  • Finalmente, agregamos nuestros estados a nuestros <AuthContext.Provider/> para que podamos acceder a ellos desde cualquier parte de nuestra aplicación cubierta por la API de contexto.

Ahora que tenemos nuestra función, es hora de volver a nuestro AppNavigator.js y escribir el código para montar un navegador de pila particular basado en el estado de autenticación:

navegación > AppNavigator.js

Primero, importaremos todo lo que necesitamos para nuestro 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";

Ahora que tenemos todas nuestras importaciones, creemos la función AppNavigator() .

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

A continuación, continuaremos escribiendo el contenido de nuestra función 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

En el bloque de código anterior, aquí hay un resumen de lo que hicimos:

  • Creamos un navegador de pila y desestructuramos el Navigator y la Screen a partir de él.
  • Importamos el isLoading userToken nuestro AuthContext
  • Cuando se monta AuthState , se llama a checkAuthenticationStatus() en el gancho useEffecct allí. Usamos la declaración if para verificar si isLoading es true , si es true , la pantalla que devolvemos es nuestra <TransitionScreen /> que creamos anteriormente porque la función checkAuthenticationStatus() aún no está completa.
  • Una vez que nuestro checkAuthenticationStatus() está completo, isLoading se establece en false y devolvemos nuestros componentes principales de navegación.
  • El NavigationContainer se importó desde @react-navigation/native . Solo se usa una vez en el navegador principal de nivel superior. Tenga en cuenta que no estamos usando esto en preAuthNavigator.js o postAuthNavigator.js.
  • En nuestro AppNavigator() , todavía creamos un navegador de pila. Si el userToken de usuario obtenido de nuestra API de contexto es null , PreAuthNavigator , si su valor es otro (lo que significa que AsyncStorage.getItem() en checkAuthenticationStatus() devolvió un valor real), entonces PostAuthNavigator . Nuestra representación condicional se realiza mediante el operador ternario.

Ahora hemos configurado nuestro AppNavigator.js . A continuación, debemos pasar nuestro AppNavigator a nuestro archivo App.js.

Pasemos nuestro AppNavigator al archivo App.js :

Aplicación.js

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

Veamos ahora cómo se ve nuestra aplicación en este momento:

Esto es lo que sucede cuando proporciona una credencial incorrecta al intentar iniciar sesión:

Adición de la funcionalidad de cierre de sesión

En este punto, nuestro proceso de autenticación y selección de ruta está completo. Lo único que queda para nuestra aplicación es agregar la funcionalidad de cierre de sesión.

El botón de cierre de sesión está en el archivo HomeScreen.js . Pasamos una función onLogout() al atributo onPress del botón. Por ahora, tenemos una simple instrucción console.log() en nuestra función, pero eso cambiará dentro de poco.

Ahora, vayamos a nuestro AuthState.js y escribamos la función para cerrar sesión. Esta función simplemente borra AsyncStorage donde se guarda el token de usuario.

contexto > authContext > AuthState.js

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

userSignout() es una función asíncrona que elimina el user-token de nuestro AsyncStorage .

Ahora debemos llamar a la función userSignout() en nuestro HomeScreen.js cada vez que se haga clic en el botón de cierre de sesión.

Vayamos a nuestro HomeScreen.js y usemos userSignout() de nuestro AuthContext .

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

En el bloque de código anterior, importamos el gancho useContext de ReactJS, luego importamos nuestro AuthContext. A continuación, desestructuramos la función userSignout de nuestro AuthContext y esta función userSignout() se llama en nuestra función onLogout() .

Ahora, cada vez que se hace clic en nuestro botón de cierre de sesión, se borra el token de usuario en nuestro AsyncStorage .

¡Voila! todo nuestro proceso ha terminado.

Esto es lo que sucede cuando presiona el botón Atrás después de iniciar sesión:

Al presionar el botón Atrás después de iniciar sesión en la aplicación.

Esto es lo que sucede cuando presiona el botón Atrás después de cerrar la sesión:

Al presionar el botón Atrás después de cerrar sesión en la aplicación.

Aquí hay algunos comportamientos diferentes que notamos cuando usamos este patrón en nuestro cambio de pila de navegación:

  1. Notará que no había ningún lugar en el que necesitáramos hacer uso de navigation.navigate() o navigation.push() para ir a otra ruta después de iniciar sesión. Una vez que nuestro estado se actualiza con el token de usuario, la pila de navegación representada cambia automáticamente.
  2. Al presionar el botón Atrás en su dispositivo después de que el inicio de sesión se haya realizado correctamente, no puede regresar a la página de inicio de sesión, sino que cierra la aplicación por completo. Este comportamiento es importante porque no desea que el usuario pueda volver a la página de inicio de sesión a menos que cierre sesión en la aplicación. Lo mismo se aplica al cierre de sesión: una vez que el usuario cierra sesión, no puede usar el botón Atrás para volver a la pantalla de HomeScreen , sino que la aplicación se cierra.

Conclusión

En muchas aplicaciones, la autenticación es una de las partes más importantes porque confirma que la persona que intenta acceder al contenido protegido tiene derecho a acceder a la información. Aprender a hacerlo bien es un paso importante en la creación de una aplicación excelente, intuitiva y fácil de usar/navegar.

Sobre la base de este código, aquí hay algunas cosas que podría considerar agregar:

  • Validación de formularios para validar campos de entrada. Consulte la validación de formularios React Native con Formik y Yup.
  • Autenticación de Firebase para integrar la autenticación con Gmail, Github, Facebook, Twitter o su interfaz personalizada. Echa un vistazo a React Native Firebase.
  • Conceptos de código para diseñadores: autenticación y autorización.

Aquí también hay algunos recursos importantes que encontré que lo iluminarán más sobre la autenticación, la seguridad y cómo hacerlo bien:

Recursos

  • React Native: flujo de autenticación de usuario explicado
  • 10 mejores prácticas de seguridad de React
  • Métodos de autenticación que pueden prevenir la próxima infracción
  • Vea una compilación/vista previa en vivo de nuestra aplicación aquí;
  • Ver el proyecto en GitHub.