Autenticação no React Native / ReactJS com Context API & Hooks

Thiago Marinho

Conheça o Rocketseat Para Empresas
Oferecemos soluções personalizadas para empresas de todos os portes.
Falaaaa Dev!!! 👊
Vamos construir juntos a funcionalidade completa de autenticação dentro do React Native, mostrando como que a gente faz a chamada API, como pega o token, usuário, como armazena os dados no Async Storage, como criar um contexto para deixar essas informações disponibilizadas em toda aplicação, como criar um custom hook, como controlar o estado de loading das informações que ainda não foram recuperadas do Storage, aonde que a gente faz autenticação, onde que a gente faz logout, o que a gente faz com Async Storage no momento que a gente fizer SignIn, SignOut ou quando a tela principal é carregada, como determinar qual stack de rotas que vamos utilizar de acordo com estado do usuário logado ou deslogado. Uauu, fluxo completo!
Estou muito empolgado com o conteúdo, vamos falar sobre tecnologias bem legais como React, React Native, a fantástica Context API, Hooks (Effect/State), Typescript, e ainda vamos criar o nosso Custom Hook!
Vai ser um passo a passo bem mastigadinho e completo!
Então, bora codar! 👨💻👩💻
Se o seu ambiente de desenvolvimento mobile com React Native não estiver pronto, você pode fazer configurar seguindo os passos doc de ambiente React Native
Criando o Projeto em React Native com Typescript
Vamos começar criando projeto React Native com Typescript, a melhor maneira de criar o projeto é executar o comando com npx:
npx react-native init authrn --template react-native-template-typescript
Com
npx ele busca o pacote na web instala na sua máquina na versão mais atualizada, executa o comando react-native, deixa em cache por um tempo e depois desinstala, dessa forma você não precisa ficar com o react-native cli na node_modules principal da sua máquina.Vamos utilizar typescript por isso passei a flag:
--template react-native-template-typescriptEu vou utilizar o simulador do iOS aqui, você pode utilizar o do Android ou até mesmo criar um projeto com Expo, isso não vai impedir de você seguir o tutorial.
Então depois de criar o projeto, posso executar:
cd authrn && yarn ios
Esse comando vai abrir a pasta do projeto e rodar o comando que faz o build no simulador do iOS, por baixo dos panos seria um
npx react-native run-ios ou react-native run-ios.Quando projeto estiver instalado e rodando você vai ver na tela o metro bundler e o simulador.
Metro bundler é o webpack do react native, responsável pela conversão do código typescript -> javascript otimizado, para ficar legível pelo react native. Se a janela não abrir, basta você rodar o comando
yarn start que provavelmente já vai estar funcionando.
Agora só abrir o projeto com vscode ou seu editor preferido!
Codando e estruturando o projeto
Deleto o arquivo App.tsx da raiz do projeto e crio uma pasta
src/ e o arquivo App.tsx dentro da pasta.Dentro do arquivo
App.tsx coloco:import React from 'react'; import {View} from 'react-native'; const App: React.FC = () => { return <View />; }; export default App;
E no arquivo
index.js na raiz do projeto altero o import do App:import App from './src/App';
Se der algum erro nessa parte, fecha o metro bundler e tenta executar novamente:
yarn start --reset-cache
O app vai ficar em branco no emulador ou simulador, mas agora vamos instalar algumas libs (bibliotecas) para continuar o app.
Navegação é com React Navigation no RN.
Vamos instalar React Navigation que é o módulo de navegação no React Native.
yarn add @react-navigation/native
E depois instalamos algumas libs auxiliares:
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
Se você estiver no macbook e estiver rodando no simulador do iOS precisa rodar:
cd ios && pod install
No Android já está tudo certo! 🙂
Agora falta adicionar o
import 'react-native-gesture-handler'; no comecinho do arquivo principal, para deixar a tela touchable (tocável):src/App.tsx:
import 'react-native-gesture-handler'; // ... restante do código omitido
E por fim, nosso App tem que ser envolvido pelo
NavigationContainer conforme a documentação.Na tela não surge nenhum efeito e o nosso código fica assim:
src/App.tsx:
import 'react-native-gesture-handler'; import React from "react"; import { View } from "react-native"; import { NavigationContainer } from "@react-navigation/native"; const App: React.FC = () => { return ( <NavigationContainer> <View /> </NavigationContainer> ); }; export default App;
Depois devemos executar o comando
npx react-native run-ios ou npx react-native run-android devido a instalação de bibliotecas nativas que fizemos.Na seção hello-react-navigation, a documentação nos ensina como configurar cada tipo de navegação.
A mais utilizada é a Stack Navigation, que para cada clique ou uma ação de navegação, ela vai sendo salva em uma pilha, e as rotas são empilheiradas, você pode voltar sempre para o estado anterior. Você pode ler um post da Rockeseat sobre navegação.
Vamos instalar a Stack Navigation:
npm install @react-navigation/stack
Agora vamos criar nossas páginas e as rotas:
Dentro da pasta
src crio as pastas pages e routes, dentro de pages vai ficar a pasta SignIn com o arquivo index.tsx e dentro de Dashboard o arquivo index.tsx.
Estrutura de pastas do projeto
O Dashboard vai ser acessado após o login (autenticação).
Por enquanto os arquivos vão ser bem simples mesmo.
src/SignIn/index.tsx:
import React from "react"; import { View } from "react-native"; const SignIn: React.FC = () => <View />; export default SignIn;
src/Dashboard/index.tsx:
import React from "react"; import { View } from "react-native"; const Dashboard: React.FC = () => <View />; export default Dashboard;
Na pasta
routes, vamos criar dois arquivos: app.routes.tsx e auth.routes.tsx.No arquivo
auth.routes.tsx ficarão as rotas não autenticadas, ou seja, as rotas que o usuário pode acessar sem precisar estar logado, o no app.routes.tsx as rotas autenticadas, que só após o logon será possível acessa-las.import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; import SignIn from '../pages/SignIn'; const AuthStack = createStackNavigator(); const AuthRoutes: React.FC = () => ( <AuthStack.Navigator> <AuthStack.Screen name="SignIn" component={SignIn} /> </AuthStack.Navigator> ); export default AuthRoutes;
createStackNavigator é a função que cria a navegação em pilha, eu invoco essa função passando o retorno dela para constante AuthStack. AuthStack se torna um componente que possuí um Navigator e Screen.Sempre o
Navigator vai envolver a Screen, e a Screen recebe duas propriedades: name e component, o name pode ser qualquer nome, geralmente uso o mesmo nome do componente e a propriedade component recebe o componente em si, o name vai ser referenciado sempre que quisermos navegar para o componente.AuthRoutes é o nome que dei para o componente de rotas da aplicação, e exporto esse componente.E o arquivo
Dashboard.tsx vai ficar quase que do mesmo jeito:import React from 'react'; import {createStackNavigator} from '@react-navigation/stack'; import Dashboard from '../pages/Dashboard'; const AppStack = createStackNavigator(); const AppRoutes: React.FC = () => ( <AppStack.Navigator> <AppStack.Screen name="Dashboard" component={Dashboard} /> </AppStack.Navigator> ); export default AppRoutes;
Pronto, agora criamos as duas stacks de navegação, uma para usuário logado e outra para deslogado.
Função de Autenticação
Por hora, vamos criar uma função fake para retornar um token de autenticação, simulando o logon da aplicação.
Criei a pasta
services e um arquivo auth.ts:src/services/auth.ts:
export function signIn() { return new Promise((resolve) => { setTimeout(() => { resolve({ token: 'jk12h3j21h3jk212h3jk12h3jkh12j3kh12k123hh21g3f12f3', name: 'Thiago', email: 'thiagomarinho@rockeseat.com.br', }); }, 2000); }); }
Quando eu chamo essa função (sem parâmetro mesmo), ela devolve uma promise (promessa) que é resolvida depois de dois segundo, trazendo um objeto com atributos token, name e email. Ficou simulando o login mesmo, certo!
Controle de Rotas da aplicação
Agora crio um arquivo
index.tsx dentro da pasta routes que vai controlar qual a rota ficará disponível, ou seja, quando o usuário estiver logado ele o roteador deve disponibilizar as rotas de dentro do app.routes.tsx quando o usuário estiver deslogado o roteador deverá disponibilizar as rotas de dentro de auth.routes.tsx.src/routes/index.tsx:
import React from 'react'; import AuthRoutes from '../routes/auth.routes'; // import AppRoutes from '../routes/app.routes'; const Routes: React.FC = () => { return <AuthRoutes />; }; export default Routes;
Por enquanto, estou criando apenas um componente Routes que retorna apenas o componente AuthRoutes, logo mais iremos validar qual será retornado, de acordo com usuário logado ou não.
Por hora, vamos apenas exibir na tela o conteúdo de dentro de SignIn, mas antes vou colocar um botão para exibir na tela:
src/pages/SignIn/index.tsx:
import React from 'react'; import {View, Button, StyleSheet} from 'react-native'; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', }, }); const SignIn: React.FC = () => ( <View style={styles.container}> <Button title="Sign In" onPress={() => {}} /> </View> ); export default SignIn;
E dentro do arquivo src/App.tsx eu importo o Routes e utilizo-o.
import React from "react"; import { NavigationContainer } from "@react-navigation/native"; import Routes from "./routes"; const App: React.FC = () => { return ( <NavigationContainer> <Routes /> </NavigationContainer> ); }; export default App;
Dessa forma agora quando eu acessar a aplicação, o Routes vai ser chamado e dentro dele vai ser retornado o AuthRoutes o qual retorna a navegação para o componente
SignIn. Por enquanto esse é o fluxo.Agora você já consegue ver um botão
Sign In na tela, que ainda não faz nada porque não coloquei nenhuma função no onPress do botão.
Ah, veja que tem um header ali escrito SignIn em negrito, isso é automático feito pelo StackNavigator, podemos remover isso ou alterar o estilo, ele é bem customizável. 👌
Agora vamos precisamos dar vida ao clique do botão no Sign In.
Então vamos mudar um pouco o arquivo SignIn/index.tsx, vamos precisar invocar a função de
signIn que criamos no arquivo src/services/auth.ts.import React from "react"; import { View, Button, StyleSheet } from "react-native"; import { signIn } from "../../services/auth"; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: "center", }, }); const SignIn: React.FC = () => { async function handleSign() { // email, password (formulário omitido) const response = await signIn(); console.log(response); } return ( <View style={styles.container}> <Button title="Sign In" onPress={handleSign} /> </View> ); }; export default SignIn;
Criei uma função
handleSign que invoca a função signIn(), que retorna depois de dois segundos os dados do usuário logado e imprime no console do terminal do metro bundler. Como ela retorna uma promise, ela tem que ser assíncrona, depois eu coloquei a referência da função no onPress={handleSign}.Pronto, agora o log do usuário aparece no terminal do metro bundler.
{"email": "thiagomarinho@rocketseat.com.br", "name": "Thiago", "token": "olkl12h3g12y2214kl123jh21gg"}
Tenho os dados do usuário, preciso armazenar em algum lugar para que sempre que ele navegar em alguma tela que requer autenticação ele possa entrar direto na tela ao invés de ser redirecionado para o login toda vez, vamos fazer isso mais para frente.
O Poder da Context API
Agora entra em ação o contexto, usaremos a Context API do React, poderia utilizar um Redux também, mas tem casos e casos para qual utilizar um ou outro, nessa aplicação pequena vale a pena manter o estado na Context API, é uma dependência a menos, já que ela vem com React e também ela está bem mais interessante de utilizar com os hooks, código fica mais coeso e menos verboso, você vai ver.
Vamos criar um pasta dentro de
src chamada contexts para armazenar nossos hooks de contexto de autenticação. Dentro da pasta criaremos o arquivo: auth.tsxsrc/contexts/auth.tsx:
import { createContext } from "react"; const AuthContext = createContext({ signed: true }); export default AuthContext;
Crio o contexto usando a função
createContext, atribuo para a AuthContext, a função createContext recebe um objeto, mesmo que o valor inicial seja vazio: {}, mas nesse momento vamos colocar signed: true, só para testar, conforme a aplicação ir crescendo vamos mudando.Conheça o Rocketseat Para Empresas
Oferecemos soluções personalizadas para empresas de todos os portes.
Artigos_
Explore conteúdos relacionados
Descubra mais artigos que complementam seu aprendizado e expandem seu conhecimento.
NewsletterReceba conteúdos inéditos e novidades gratuitamente
.png&w=640&q=75)