Em meus dias de iniciante na construção de aplicativos web full stack com ReactJs, fiquei confuso sobre como lidar com a autenticação no frontend. Quero dizer, o que você deve fazer depois de receber seu token de acesso do backend? Como você preserva o estado de login?
A maioria dos iniciantes presumiria “Ah, basta armazenar seu token no estado”. Mas descobri rapidamente que não era a melhor solução, nem sequer é uma solução porque, como a maioria dos desenvolvedores experientes de ReactJs sabem, o estado é temporário porque é limpo cada vez que você atualiza a página e nós definitivamente podemos' não é possível fazer login do usuário sempre que ele é atualizado.
Avanço rápido, agora que ganhei um pouco de experiência na construção de aplicativos full stack em reação, estudando a abordagem de um desenvolvedor mais experiente para autenticação e replicando o processo em dois outros aplicativos, gostaria de dar um guia sobre como eu lido com isso atualmente. Algumas pessoas podem não pensar que é o melhor caminho, mas eu o adotei por enquanto e estou aberto a aprender outros métodos usados por outros desenvolvedores.
Você enviou seu e-mail e senha (supondo que esteja usando autenticação básica de e-mail e senha) ao back-end para iniciar o processo de autenticação. Não falarei sobre como a autenticação é tratada no back-end porque este artigo é sobre como lidar com a autenticação apenas no front-end. Vou pular para a parte em que você recebeu um token na resposta HTTP. Abaixo está um exemplo de código de um componente de formulário de login simples que envia o e-mail e a senha ao servidor e recebe o token e as informações do usuário na resposta. Agora, por uma questão de simplicidade, meus valores de formulário são gerenciados com estado, seria muito melhor usar uma biblioteca robusta como o formik para aplicativos de produção.
import axios from 'axios' import { useState } from "react" export default function LoginForm() { const [email, setEmail] = useState("") const [password, setPassword] = useState("") const handleSubmit = async() => { try { const response = await axios.post("/api/auth/login", { email, password }) if (response?.status !== 200) { throw new Error("Failed login") } const token = response?.data?.token const userInfo = response?.data?.userInfo } catch (error) { throw error } } return() }
Envolva todo o seu aplicativo ou apenas as partes que precisam de acesso ao estado de autenticação em um provedor de contexto de autenticação. Isso geralmente é feito em seu arquivo App.jsx raiz. Se você não tem ideia do que é API de contexto, sinta-se à vontade para verificar a documentação do Reactjs. Os exemplos abaixo mostram um componente do provedor AuthContext criado. Em seguida, ele é importado em App.jsx e usado para agrupar o RouterProvider retornado no componente App, tornando o estado de autenticação acessível de qualquer lugar no aplicativo.
import { createContext } from "react"; export const AuthContext = createContext(null) export default function AuthProvider({children}) { return({children} ) }
import React from "react"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; import AuthProvider from "./AuthContext"; const router = createBrowserRouter([ // your routes here ]) function App() { return() } export default App
No contexto de autenticação, você deve inicializar duas variáveis de estado, “isLoggedIn” e “authenticatedUser”. O primeiro estado é um tipo booleano que será inicialmente definido como ‘false’ e depois atualizado para ‘true’ assim que o login for confirmado. A segunda variável de estado é usada para armazenar as informações do usuário conectado, como nomes, e-mail, etc. Essas variáveis de estado devem ser incluídas no valor do provedor retornado no componente de contexto para que possam ser acessíveis em todo o aplicativo para renderização condicional. .
import { createContext, useState } from "react"; export const AuthContext = createContext(null) export default function AuthProvider({children}) { const [isLoggedIn, setIsLoggedIn] = useState(false) const [authenticatedUser, setAuthenticatedUser] = useState(null) const values = { isLoggedIn, authenticatedUser, setAuthenticatedUser } return({children} ) }
Nanostores é um pacote para gerenciar estado em aplicativos Javascript. O pacote fornece uma API simples para gerenciar valores de estado em vários componentes, simplesmente inicializando-o em um arquivo separado e importando-o em qualquer componente onde você deseja usar o estado ou atualizá-lo. Mas, com a finalidade de armazenar seu token de autenticação recebido na resposta HTTP na etapa um, você usará nanostores/persistentes. Este pacote persiste em seu estado armazenando-o em localStorage, dessa forma ele não é apagado quando você atualiza a página. @nanostores/react é uma integração específica de reação para nanostores, disponibiliza o gancho useStore para extrair valores de um estado nanostore.
Então agora você pode ir em frente e:
Instale os seguintes pacotes: nanostores, @nanostores/persistent e @nanostores/react.
Em um arquivo separado chamado user.atom.js ou qualquer nome que você escolher, inicialize um armazenamento ‘authToken’ e um armazenamento ‘user’ usando nanostores/persistent.
Importe-os para o arquivo do componente do formulário de login e atualize o estado com o token e os dados do usuário recebidos em sua resposta de login.
npm i nanostores @nanostores/persistent @nanostores/react
import { persistentMap } from '@nanostores/persistent' export const authToken = persistentMap('token', null) export const user = persistentMap('user', null)
import { authToken, user } from './user.atom' const handleSubmit = async() => { try { const response = await axios.post("/api/auth/login", { email, password }) if (response?.status !== 200) { throw new Error("Failed login") } const token = response?.data?.token const userInfo = response?.data?.userInfo authToken.set(token) user.set(userInfo) } catch (error) { throw error } }
Agora, no contexto de autenticação que envolve seu aplicativo, você precisa garantir que o token e os estados do usuário sejam mantidos atualizados e disponibilizados em todo o aplicativo. Para conseguir isso, você deve:
Importe as lojas ‘authToken’ e ‘user’.
Inicialize um gancho useEffect, dentro do gancho, crie uma função 'checkLogin()' que irá verificar se o token está presente no armazenamento 'authToken', se estiver, execute uma função para verificar se está expirado. Com base nos resultados da verificação, você redireciona o usuário para a página de login para ser autenticado OU… define o estado ‘isLoggedIn’ como verdadeiro. Agora, para garantir que o estado de login seja rastreado com mais frequência, esse gancho pode ser configurado para ser executado sempre que o caminho atual mudar, dessa forma, um usuário pode ser expulso ou redirecionado para a página de login se seu token expirar durante a interação com o aplicativo .
Inicialize outro gancho useEffect que conterá uma função para buscar as informações do usuário do back-end usando o token no armazenamento authToken sempre que o aplicativo for carregado ou atualizado. Se você receber uma resposta bem-sucedida, defina o estado ‘isLoggedIn’ como verdadeiro e atualize o estado ‘authenticatedUser’ e o armazenamento ‘user’ com as informações do usuário recebidas na resposta.
Abaixo está o arquivo do componente AuthProvider atualizado.
import { createContext, useState } from "react"; import { authToken, user } from './user.atom'; import { useStore } from "@nanostores/react"; import { useNavigate, useLocation } from "react-router-dom"; import axios from "axios"; export const AuthContext = createContext(null) export default function AuthProvider({children}) { const [isLoggedIn, setIsLoggedIn] = useState(false) const [authenticatedUser, setAuthenticatedUser] = useState(null) const token = useStore(authToken) const navigate = useNavigate() const { pathname } = useLocation() function isTokenExpired() { // verify token expiration and return true or false } // Hook to check if user is logged in useEffect(() => { async function checkLogin () { if (token) { const expiredToken = isTokenExpired(token); if (expiredToken) { // clear out expired token and user from store and navigate to login page authToken.set(null) user.set(null) setIsLoggedIn(false); navigate("/login"); return; } } }; checkLogin() }, [pathname]) // Hook to fetch current user info and update state useEffect(() => { async function fetchUser() { const response = await axios.get("/api/auth/user", { headers: { 'Authorization': `Bearer ${token}` } }) if(response?.status !== 200) { throw new Error("Failed to fetch user data") } setAuthenticatedUser(response?.data) setIsLoggedIn(true) } fetchUser() }, []) const values = { isLoggedIn, authenticatedUser, setAuthenticatedUser } return({children} ) }
Agora, esses dois ganchos useEffect criados na etapa cinco são responsáveis por manter o estado de autenticação de todo o seu aplicativo gerenciado. Cada vez que você faz uma atualização, eles são executados para verificar seu token no armazenamento local, recuperar os dados mais atuais do usuário diretamente do back-end e atualizar seu estado ‘isLoggedIn’ e ‘authenticatedUser’. Você pode usar os estados dentro de qualquer componente importando o gancho ‘AuthContext’ e ‘useContext’ do react e chamando-os dentro do seu componente para acessar os valores e usá-los para alguma renderização condicional.
Bem-vindo {authenticatedUser?.name}
:import { useContext } from "react"; import { AuthContext } from "./AuthContext"; export default function MyLoggedInComponent() { const { isLoggedIn, authenticatedUser } = useContext(AuthContext) return( { isLoggedIn ?Lembre-se, ao sair, você deve limpar o armazenamento ‘authToken’ e ‘user’, definindo-os como nulos. Você também precisa definir ‘isLoggedIn’ como falso e ‘authenticatedUser’ como nulo.Welcome {authenticatedUser?.name}
: } > ) }
Obrigado pela leitura!
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3