Manipulação de montagem e desmontagem de rotas de navegação em React Native
Publicados: 2022-03-10Neste 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:
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:
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:
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 manipuladoronChangeText
. - O setter do estado que deve ser atualizado (para o primeiro campo de entrada passamos
setEmail
e o segundo passamossetPassword
. - Finalmente, escrevemos nossa função
onInputChange
, e nossa função faz apenas uma coisa: atualiza os respectivos estados com o novo valor.
- O
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
:
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
eisLoading
. O estadouserToken
será usado para armazenar o token salvo emAsyncStorage
, enquanto o estadoisLoading
será usado para rastrear o status de carregamento (inicialmente é definido comotrue
). Descobriremos mais sobre o uso desses dois estados à medida que prosseguirmos.Em seguida, escrevemos nossa função
onAuthentication()
. Essa função é uma funçãoasync
que é chamada quando o botão de login é clicado no arquivowelcomeScreen.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 chamadaUSER_TOKEN
.Em seguida, usamos a palavra-chave
await
para definir nosso token de usuário como AsyncStorage com o nomeuser-token
. A instruçãoconsole.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. OcreateStackNavigator
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
eScreen
a partir docreateStackNavigator()
. - 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:
- 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 exibirloading…
. -
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 deAsyncStorage
. ComAsyncStorage
, se não houver nenhum token fornecido, ele retornaránull
. Nosso estado inicialuserToken
também é definido comonull
. - Usamos o
setUserToken
para definir nosso valor retornado deAsyncStorage
como nosso novouserToken
. Se o valor retornado fornull
, isso significa que nossouserToken
permanecenull
. - Após o bloco
try{}…catch(){}
, definimosisLoading
como false porque a função para verificar o status da autenticação foi concluída. Precisaremos do valor deisLoading
para saber se ainda devemos exibir aTransitionScreen
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 ouseEffect()
ReactJS para fazer isso. Chamamos nossa funçãocheckAuthenticationStatus()
dentro do ganchouseEffect()
e definimos o valor deisLoading
comofalse
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 aScreen
a partir dele. - Importamos o
userToken
e oisLoading
do nossoAuthContext
- Quando o
AuthState
montado, ocheckAuthenticationStatus()
é chamado no ganchouseEffecct
. Usamos a instruçãoif
para verificar seisLoading
étrue
, se fortrue
a tela que retornamos é nossa<TransitionScreen />
que criamos anteriormente porque a funçãocheckAuthenticationStatus()
ainda não está completa. - Assim que nosso
checkAuthenticationStatus()
estiver concluído,isLoading
é definido comofalse
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 ouserToken
obtido da nossa Context API fornull
, montamos oPreAuthNavigator
, se seu valor for outro (significa que oAsyncStorage.getItem()
nocheckAuthenticationStatus()
retornou um valor real), então montamos oPostAuthNavigator
. 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:
Veja o que acontece quando você pressiona o botão Voltar após sair:
Aqui estão alguns comportamentos diferentes que notamos ao usar esse padrão em nossa mudança de pilha de navegação:
- Você notará que não havia em nenhum lugar que precisávamos fazer uso de
navigation.navigate()
ounavigation.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. - 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.