Manipulação de montagem e desmontagem de rotas de navegação em React Native

Publicados: 2022-03-10
Resumo rápido ↬ Muitas vezes você precisa de dois conjuntos diferentes de pilhas de navegação para pré e pós-autenticação do usuário. Normalmente, para ver mais conteúdo, você precisa estar autenticado de alguma forma. Vamos ver como montar e desmontar a pilha de navegação com base em uma condição atendida no React Native.

Neste artigo, vamos percorrer a montagem e desmontagem de rotas de navegação no React Native. Um comportamento esperado do seu aplicativo é que, uma vez que a condição de autenticação seja atendida, um novo conjunto de rotas de navegação esteja disponível apenas para usuários logados, enquanto as outras telas que foram exibidas antes da autenticação são removidas e não podem ser retornadas a menos que o usuário sai do aplicativo.

Para segurança em seu aplicativo, as rotas protegidas fornecem uma maneira de exibir apenas determinadas informações/conteúdos em seu aplicativo para usuários específicos, enquanto restringe o acesso de pessoas não autorizadas.

Estaremos trabalhando com a Expo para este projeto porque isso nos ajudará a focar no problema em questão, em vez de nos preocuparmos com muitas configurações. As mesmas etapas neste artigo podem ser seguidas para um aplicativo React Native simples.

Você precisa de alguma familiaridade com JavaScript e React Native para seguir com este tutorial. Aqui estão algumas coisas importantes com as quais você já deve estar familiarizado:

  • Componentes personalizados em React Native (como criar componentes, receber, passar e usar props em um componente). Consulte Mais informação.
  • Reagir Navegação. Consulte Mais informação.
  • Stack Navigator em React Native. Consulte Mais informação.
  • Conhecimento básico dos componentes React Native Core ( <View/> , <Text/> , etc.). Consulte Mais informação.
  • Reagir AsyncStorage nativo . Consulte Mais informação.
  • API de contexto. Consulte Mais informação.

Configuração do projeto e autenticação de base

Se você é novo no uso do expo e não sabe como instalar o expo, visite a documentação oficial. Quando a instalação estiver concluída, vá em frente para inicializar um novo projeto React Native com expo em nosso prompt de comando:

 expo init navigation-project

Você verá algumas opções para escolher como deseja que a configuração básica seja:

Configuração base do projeto React Native
(Visualização grande)

No nosso caso, vamos selecionar a primeira opção para configurar nosso projeto como um documento em branco. Agora, espere até que a instalação das dependências do JavaScript seja concluída.

Depois que nosso aplicativo estiver configurado, podemos alterar nosso diretório para nosso novo diretório de projeto e abri-lo em seu editor de código favorito. Precisamos instalar a biblioteca que usaremos para AsyncStorage e nossas bibliotecas de navegação. Dentro do diretório da sua pasta em seu terminal, cole o comando acima e escolha um template ( blank funcionaria) para instalar nossas dependências do projeto.

Vejamos para que serve cada uma dessas dependências:

  • @react-native-community/async-storage
    Como localStorage na web, é uma API React Native para dados persistentes em um dispositivo em pares de valores-chave.
  • @react-native-community/masked-view, react-native-screens, react-native-gesture-handle
    Essas dependências são utilitários principais usados ​​pela maioria dos navegadores para criar a estrutura de navegação no aplicativo. (Leia mais em Introdução à navegação React Native.)
  • @react-navigation/native
    Esta é a dependência para navegação React Native.
  • @react-navigation/stack
    Esta é a dependência para navegação de pilha no 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 o aplicativo, use o expo start no diretório do aplicativo em seu terminal. Uma vez que o aplicativo é iniciado, você pode usar o aplicativo expo do seu celular para escanear o código de barras e visualizar o aplicativo, ou se você tiver um emulador de android/simulador de IOS, você pode abrir o aplicativo através da ferramenta de desenvolvedor expo que abre em seu navegador quando você inicia um aplicativo expo. Para os exemplos de imagens neste artigo, usaremos o Genymotions para ver nosso resultado. Veja como será nosso resultado final no Genymotions:

resultado final em Genymotions
(Visualização grande)

Estruturas de pastas

Vamos criar nossa estrutura de pastas desde o início para que seja mais fácil trabalhar com ela à medida que avançamos:

Precisamos de duas pastas primeiro:

  • contexto
    Essa pasta conterá o contexto de todo o nosso aplicativo, pois trabalharemos com a API de contexto para gerenciamento de estado global.
  • Visualizações
    Esta pasta conterá a pasta de navegação e as visualizações para diferentes telas.

Vá em frente e crie as duas pastas no diretório do seu projeto.

Dentro da pasta context, crie uma pasta chamada authContext e crie dois arquivos dentro da pasta authContext :

  • AuthContext.js ,
  • AuthState.js .

Precisaremos desses arquivos quando começarmos a trabalhar com a Context API.

Agora vá para a pasta views que criamos e crie mais duas pastas dentro dela, a saber:

  • navegação ,
  • telas .

Agora, ainda não terminamos, dentro da pasta screens , crie mais essas duas pastas:

  • postAuthScreens ,
  • preAuthScreens .

Se você seguiu a configuração da pasta corretamente, é assim que sua estrutura de pastas deve estar no momento:

estrutura de pastas
(Visualização grande)
Mais depois do salto! Continue lendo abaixo ↓

Criando nossa primeira tela

Agora vamos criar nossa primeira tela e chamá-la de welcomeScreen.js dentro da pasta preAuthScreens .

preAuthScreens > welcomeScreen.js

Este é o conteúdo do nosso arquivo 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

Aqui está o que fizemos no bloco de código acima:

Primeiro, importamos as coisas que precisamos da biblioteca React Native, ou seja, View , Text , Button , TextInput . Em seguida, criamos nosso componente funcional WelcomeScreen .

Você notará que importamos o StyleSheet do React Native e o usamos para definir estilos para nosso cabeçalho e também nosso <TextInput /> .

Por fim, exportamos o componente WelcomeScreen na parte inferior do código.

Agora que terminamos com isso, vamos fazer com que esse componente funcione conforme o esperado usando o gancho useState para armazenar os valores das entradas e atualizar seus estados sempre que ocorrer uma alteração nos campos de entrada. Também traremos import o hook useCallback do React, pois precisaremos dele mais tarde para manter uma função.

Primeiro, enquanto ainda estamos no componente WelcomeScreen , precisamos importar o useState e useCallback do React.

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

Agora dentro do componente funcional WelcomeScreen , vamos criar os dois estados para email e senha respectivamente:

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

Em seguida, precisamos modificar nossos campos <TextInput /> para que obtenham seu valor de seus respectivos estados e atualizem seu estado quando o valor da entrada for atualizado:

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

No código acima, aqui está o que fizemos:

  • Fizemos o value de cada uma das entradas de texto para apontar para seus respectivos estados.
  • Adicionamos o manipulador onChangeText às nossas entradas de texto. Isso é acionado sempre que um novo valor é inserido ou excluído dos campos de entrada.
  • Chamamos nossa função onInputChange que aceita dois argumentos:
    • O value atual é fornecido pelo manipulador onChangeText .
    • O setter do estado que deve ser atualizado (para o primeiro campo de entrada passamos setEmail e o segundo passamos setPassword .
    • Finalmente, escrevemos nossa função onInputChange , e nossa função faz apenas uma coisa: atualiza os respectivos estados com o novo valor.

A próxima coisa que precisamos trabalhar é a função onUserAuthentication() que é chamada sempre que o botão para o envio do formulário é clicado.

Idealmente, o usuário já deve ter criado uma conta e o login envolverá algum tipo de lógica de back-end para verificar se o usuário existe e, em seguida, atribuir um token ao usuário. No nosso caso, como não estamos usando nenhum backend, criaremos um objeto contendo os detalhes corretos de login do usuário e, em seguida, autenticaremos um usuário apenas quando os valores inseridos corresponderem aos nossos valores fixos do objeto login de email e password que iremos crio.

Aqui está o código que precisamos para fazer isso:

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

Uma das primeiras coisas que você notará no código acima é que definimos um correctAuthenticationDetails (que é um objeto que contém os detalhes de login corretos que esperamos que um usuário forneça) fora do componente funcional WelcomeScreen() .

Em seguida, escrevemos o conteúdo da função onUserAuthentication() e usamos uma instrução condicional para verificar se o email ou password mantidos nos respectivos estados não corresponde ao que fornecemos em nosso objeto.

Se você quiser ver o que fizemos até agora, importe o componente WelcomeScreen para seu App.js assim:

Abra o arquivo App.js e coloque este substitua todo o código por este:

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

Observando atentamente o código acima, você verá que o que fizemos foi importar o componente WelcomeScreen e depois usá-lo na função App() .

Veja como é o resultado do nosso WelcomeScreen :

o resultado do WelcomeScreen
(Visualização grande)

Agora que terminamos de construir o componente WelcomeScreen , vamos seguir em frente e começar a trabalhar com a Context API para gerenciar nosso estado global.

Por que API de contexto?

Usando a Context API, não precisamos instalar nenhuma biblioteca adicional no ReactJS, é menos estressante para configurar e é uma das formas mais populares de lidar com o estado global no ReactJS. Para gerenciamento de estado leve, é uma boa escolha.

Criando nosso contexto

Se você se lembra, criamos uma pasta de contexto anteriormente e criamos uma subpasta dentro dela chamada authContext .

Agora vamos navegar até o arquivo AuthContext.js na pasta authContext e criar nosso contexto:

contexto > authContext > AuthContext.js

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

O AuthContext que acabamos de criar contém o valor do estado de loading e os valores do estado userToken . Atualmente, no createContext que declaramos no bloco de código acima, não inicializamos nenhum valor padrão aqui, então nosso contexto está undefined no momento. Um valor de exemplo do contexto de autenticação pode ser {loading: false, userToken: 'abcd}

O arquivo AuthState.js contém nossa lógica de API de contexto e seus valores de estado. As funções escritas aqui podem ser chamadas de qualquer lugar em nosso aplicativo e quando atualizam valores no estado, ele também é atualizado globalmente.

Primeiro, vamos trazer todas as importações que precisaremos neste arquivo:

contexto > AuthContext > AuthState.js

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

Importamos o useState() do ReactJS para manter nossos estados, importamos o arquivo AuthContext que criamos acima porque é onde nosso contexto vazio para autenticação é inicializado e precisaremos usá-lo como você verá mais adiante enquanto progredimos , finalmente importamos o pacote AsyncStorage (semelhante ao localStorage para a web).

AsyncStorage é uma API React Native que permite que você persista dados offline no dispositivo em um aplicativo 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;

No bloco de código acima, aqui está o que fizemos:

  • Declaramos dois estados para userToken e isLoading . O estado userToken será usado para armazenar o token salvo em AsyncStorage , enquanto o estado isLoading será usado para rastrear o status de carregamento (inicialmente é definido como true ). Descobriremos mais sobre o uso desses dois estados à medida que prosseguirmos.

  • Em seguida, escrevemos nossa função onAuthentication() . Essa função é uma função async que é chamada quando o botão de login é clicado no arquivo welcomeScreen.jsx . Essa função só será chamada se o e-mail e a senha que o usuário forneceu corresponderem ao objeto de detalhes do usuário correto que fornecemos. Normalmente, o que acontece durante a autenticação é que um token é gerado para o usuário depois que o usuário é autenticado no backend usando um pacote como JWT, e esse token é enviado para o frontend. Como não vamos abordar tudo isso neste tutorial, criamos um token estático e o mantivemos em uma variável chamada USER_TOKEN .

  • Em seguida, usamos a palavra-chave await para definir nosso token de usuário como AsyncStorage com o nome user-token . A instrução console.warn() é usada apenas para verificar se tudo deu certo, você pode tirá-la quando quiser.

  • Por fim, passamos nossa função onAuthenticated como um valor dentro de nosso <AuthContext.Provider> para que possamos acessar e chamar a função de qualquer lugar em nosso aplicativo.

screens > preAuth > welcomeScreen.js

Primeiro, importe useContext do ReactJS e importe o AuthContext do arquivo AuthContext.js .

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

Agora, dentro do componente funcional welcomeScreen() , vamos usar o contexto que criamos:

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

No bloco de código acima, desestruturamos a função onAuthentication de nosso AuthContext e então a chamamos dentro de nossa função onUserAuthentication() e removemos a instrução console.log() que estava lá antes.

No momento, isso gerará um erro porque ainda não temos acesso ao AuthContext . Para usar o AuthContext em qualquer lugar em seu aplicativo, precisamos envolver o arquivo de nível superior em nosso aplicativo com o AuthState (no nosso caso, é o arquivo App.js ).

Vá para o arquivo App.js e substitua o código por este:

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

Chegamos até aqui e terminamos esta seção. Antes de passarmos para a próxima seção onde configuramos nosso roteamento, vamos criar uma nova tela. A tela que estamos prestes a criar será o arquivo HomeScreen.js que deve aparecer somente após a autenticação bem-sucedida.

Vá para: screens > postAuth .

Crie um novo arquivo chamado HomeScreen.js . Aqui está o código para o arquivo HomeScreen.js :

screens > 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 enquanto, o botão de logout tem uma instrução console.log() fictícia. Mais tarde, vamos criar a funcionalidade de logout e passá-la para a tela do nosso contexto.

Configurando nossas rotas

Precisamos criar três (3) arquivos dentro de nossa pasta de navegação:

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

Depois de criar esses três arquivos, navegue até o arquivo preAuthNaviagtor.js que você acabou de criar e escreva isto:

navegação > 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;

No arquivo acima, aqui está o que fizemos:

  • Importamos o createStackNavigator do @react-navigation/stack que estamos usando para nossa navegação na pilha. O createStackNavigator fornece uma maneira de seu aplicativo fazer a transição entre as telas em que cada nova tela é colocada no topo de uma pilha. Por padrão, o navegador de pilha está configurado para ter a aparência familiar do iOS e do Android: novas telas deslizam da direita no iOS, desaparecem da parte inferior no Android. Clique aqui se quiser saber mais sobre o navegador de pilha no React Native.
  • Desestruturamos Navigator e Screen a partir do createStackNavigator() .
  • Em nossa declaração de retorno, criamos nossa navegação com o <Navigator/> e criamos nossa tela com o <Screen/> . isso significa que se tivéssemos várias telas que podem ser acessadas antes da autenticação, teremos várias tags <Screen/> aqui representando-as.
  • Por fim, exportamos nosso componente PreAuthNavigator .

Vamos fazer algo semelhante para o arquivo postAuthNavigator.js .

navegação > 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 no código acima, a única diferença entre o preAuthNavigator.js e o postAuthNavigator.js é a tela que está sendo renderizada. Enquanto o primeiro pega o WelcomeScreen , o postAuthNavigator.js pega o HomeScreen .

Para criar nosso AppNavigator.js precisamos criar algumas coisas.

Como o AppNavigator.js é onde vamos alternar e verificar qual rota estará disponível para acesso do usuário, precisamos de várias telas para que isso funcione corretamente, vamos delinear o que precisamos criar primeiro:

  1. TransitionScreen.js
    Enquanto o aplicativo decide qual navegação será montada, queremos que uma tela de transição apareça. Normalmente, a tela de transição será um spinner de carregamento ou qualquer outra animação personalizada escolhida para o aplicativo, mas no nosso caso, usaremos uma tag <Text/> básica para exibir loading… .
  2. checkAuthenticationStatus()
    Esta função é o que chamaremos para verificar o status de autenticação que determinará qual pilha de navegação será montada. Vamos criar esta função em nosso contexto e usá-la no Appnavigator.js .

Agora, vamos em frente e criar nosso arquivo TransitionScreen.js .

telas > TransitionScreen.js

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

Nossa tela de transição é apenas uma tela simples que mostra o texto de carregamento. Veremos onde usar isso à medida que avançamos neste artigo.

Em seguida, vamos ao nosso AuthState.js e escreva nosso 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;

No bloco de código acima, escrevemos a função checkAuthenticationStatus() . Em nossa função, aqui está o que estamos fazendo:

  • Usamos a palavra-chave await para obter nosso token de AsyncStorage . Com AsyncStorage , se não houver nenhum token fornecido, ele retornará null . Nosso estado inicial userToken também é definido como null .
  • Usamos o setUserToken para definir nosso valor retornado de AsyncStorage como nosso novo userToken . Se o valor retornado for null , isso significa que nosso userToken permanece null .
  • Após o bloco try{}…catch(){} , definimos isLoading como false porque a função para verificar o status da autenticação foi concluída. Precisaremos do valor de isLoading para saber se ainda devemos exibir a TransitionScreen ou não. Vale a pena considerar definir um erro se houver um erro ao recuperar o token para que possamos mostrar ao usuário um botão “Repetir” ou “Tentar novamente” quando o erro for encontrado.
  • Sempre que AuthState é montado, queremos verificar o status da autenticação, então usamos o useEffect() ReactJS para fazer isso. Chamamos nossa função checkAuthenticationStatus() dentro do gancho useEffect() e definimos o valor de isLoading como false quando terminar.
  • Por fim, adicionamos nossos estados aos nossos valores <AuthContext.Provider/> para que possamos acessá-los de qualquer lugar em nosso aplicativo coberto pela Context API.

Agora que temos nossa função, é hora de voltar ao nosso AppNavigator.js e escrever o código para montar um navegador de pilha específico com base no status de autenticação:

navegação > AppNavigator.js

Primeiro, importaremos tudo o que precisamos para nosso 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";

Agora que temos todas as nossas importações, vamos criar a função AppNavigator() .

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

Em seguida, vamos agora escrever o conteúdo da nossa função 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

No bloco de código acima, aqui está um resumo do que fizemos:

  • Criamos um navegador de pilha e desestruturamos o Navigator e a Screen a partir dele.
  • Importamos o userToken e o isLoading do nosso AuthContext
  • Quando o AuthState montado, o checkAuthenticationStatus() é chamado no gancho useEffecct . Usamos a instrução if para verificar se isLoading é true , se for true a tela que retornamos é nossa <TransitionScreen /> que criamos anteriormente porque a função checkAuthenticationStatus() ainda não está completa.
  • Assim que nosso checkAuthenticationStatus() estiver concluído, isLoading é definido como false e retornamos nossos principais componentes de navegação.
  • O NavigationContainer foi importado do @react-navigation/native . Ele é usado apenas uma vez no navegador principal de nível superior. Observe que não estamos usando isso no preAuthNavigator.js ou no postAuthNavigator.js.
  • Em nosso AppNavigator() , ainda criamos um navegador de pilha. Se o userToken obtido da nossa Context API for null , montamos o PreAuthNavigator , se seu valor for outro (significa que o AsyncStorage.getItem() no checkAuthenticationStatus() retornou um valor real), então montamos o PostAuthNavigator . Nossa renderização condicional é feita usando o operador ternário.

Agora configuramos nosso AppNavigator.js . Em seguida, precisamos passar nosso AppNavigator para nosso arquivo App.js.

Vamos passar nosso AppNavigator para o arquivo App.js :

App.js

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

Vamos agora ver como está nosso aplicativo no momento:

Veja o que acontece quando você fornece uma credencial incorreta ao tentar fazer login:

Adicionando a funcionalidade de logout

Neste ponto, nosso processo de autenticação e seleção de rota está completo. A única coisa que resta para o nosso aplicativo é adicionar a funcionalidade de logout.

O botão de logout está no arquivo HomeScreen.js . Passamos uma função onLogout() para o atributo onPress do botão. Por enquanto, temos uma simples instrução console.log() em nossa função, mas em pouco tempo isso mudará.

Agora, vamos ao nosso AuthState.js e escreva a função para logout. Esta função simplesmente limpa o AsyncStorage onde o token do usuário é salvo.

contexto > authContext > AuthState.js

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

O userSignout() é uma função assíncrona que remove o user-token do nosso AsyncStorage .

Agora precisamos chamar a função userSignout() em nosso HomeScreen.js sempre que o botão de logout for clicado.

Vamos para nosso HomeScreen.js e use userSignout() do nosso AuthContext .

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

No bloco de código acima, importamos o hook useContext do ReactJS, então importamos nosso AuthContext. Em seguida, desestruturamos a função userSignout de nosso AuthContext e essa função userSignout() é chamada em nossa função onLogout() .

Agora, sempre que nosso botão de logout é clicado, o token do usuário em nosso AsyncStorage é limpo.

Voilá! todo o nosso processo está concluído.

Veja o que acontece quando você pressiona o botão Voltar depois de fazer login:

Pressionando o botão Voltar depois de fazer login no aplicativo.

Veja o que acontece quando você pressiona o botão Voltar após sair:

Pressionando o botão Voltar depois de sair do aplicativo.

Aqui estão alguns comportamentos diferentes que notamos ao usar esse padrão em nossa mudança de pilha de navegação:

  1. Você notará que não havia em nenhum lugar que precisávamos fazer uso de navigation.navigate() ou navigation.push() para ir para outra rota após o login. Depois que nosso estado é atualizado com o token do usuário, a pilha de navegação renderizada é alterada automaticamente.
  2. Pressionar o botão Voltar no seu dispositivo após o login ser bem-sucedido não pode levá-lo de volta à página de login, em vez disso, ele fecha o aplicativo completamente. Esse comportamento é importante porque você não deseja que o usuário possa retornar à página de login, exceto que ele saia do aplicativo. A mesma coisa se aplica ao logout - uma vez que o usuário faz logout, ele não pode usar o botão Voltar para retornar à tela HomeScreen , mas, em vez disso, o aplicativo é fechado.

Conclusão

Em muitos aplicativos, a autenticação é uma das partes mais importantes, pois confirma que a pessoa que tenta acessar o conteúdo protegido tem o direito de acessar as informações. Aprender como fazê-lo corretamente é um passo importante na construção de um aplicativo excelente, intuitivo e fácil de usar/navegar.

Com base nesse código, aqui estão algumas coisas que você pode considerar adicionar:

  • Validação de formulário para validação de campos de entrada. Confira a validação de formulário React Native com Formik e Yup.
  • Autenticação do Firebase para integração de autenticação com Gmail, Github, Facebook, Twitter ou sua interface personalizada. Confira React Native Firebase.
  • Conceitos de código para designers: autenticação e autorização.

Aqui também estão alguns recursos importantes que encontrei para esclarecer mais sobre autenticação, segurança e como fazer isso corretamente:

Recursos

  • React Native: fluxo de autenticação do usuário explicado
  • 10 melhores práticas de segurança do React
  • Métodos de autenticação que podem impedir a próxima violação
  • Veja uma compilação/visualização ao vivo do nosso aplicativo aqui;
  • Visualize o projeto no GitHub.