Gestão de Estado em React: Um Exemplo Prático

reagir
gerenciamento de estado
API de contexto
Gestão de Estado em React: Um Exemplo Prático cover image

React é a estrutura ideal para a construção de aplicativos dinâmicos do lado do cliente para muitos desenvolvedores. A natureza dinâmica desses aplicativos vem da flexibilidade e da extensa lista de capacidades e recursos que são possíveis no lado do cliente, o que permitiu aos desenvolvedores construir aplicativos completos que carregam no navegador em questão de segundos, um feito que não era possível. possível (ou muito complicado) na época da web estática.

Com essa expansão de possibilidades, surgiu o conceito de gerenciamento de estado, à medida que a complexidade cresce nas aplicações do lado do cliente, a necessidade de manter o estado local cresce e se torna um gargalo por si só se não for tratado corretamente e concebido com escalabilidade em mente.

Esta questão foi abordada por muitos frameworks, seguindo diferentes abordagens e focando em diferentes conjuntos de subproblemas, por isso é importante ter um entendimento de alto nível do ecossistema do framework escolhido para avaliar as necessidades de cada aplicação e empregar a abordagem correta seguindo aqueles Métricas. Este artigo fornecerá uma breve visão geral dos problemas comuns de gerenciamento de estado e tentará introduzir diferentes abordagens (useState, Context API) como resposta a eles. Embora este artigo apresente múltiplas soluções, ele se concentrará apenas em desafios em menor escala. Abordaremos assuntos mais avançados em próximos artigos.

Fluxo de trabalho de autenticação

O código apresentado ao longo do artigo pode ser encontrado aqui.

Um link de visualização ao vivo pode ser acessado aqui.

Considere o caso em que implementamos o processo de autenticação para uma aplicação React.

User Login

Conforme mostrado no GIF acima, queremos permitir que os usuários façam login ou se inscrevam em nosso aplicativo usando credenciais. Se credenciais válidas forem fornecidas, o usuário fará login, o aplicativo navegará automaticamente para a página inicial e o usuário poderá continuar a usar o aplicativo.

Da mesma forma, se o usuário efetuar logout, os recursos da página inicial serão protegidos pelo login, a página de login será a única página acessível pelo usuário.

Pensando neste fluxo de trabalho em termos de implementação, teríamos um componente principal chamado App, o componente App redirecionará o usuário para uma de duas páginas: Home ou Login, o estado atual do usuário (logado, desconectado) ditará qual página para a qual o usuário é redirecionado, uma mudança no estado atual do usuário (por exemplo, mudar de conectado para desconectado) deve acionar um redirecionamento instantâneo para a página correspondente.

State

Conforme mostrado na ilustração acima, queremos que o componente App considere o estado atual e renderize apenas uma das duas páginas – Home ou Login – com base nesse estado atual.

Se o usuário for nulo, significa que não temos nenhum usuário autenticado, então navegamos automaticamente para a página de login e protegemos a página inicial usando renderização condicional. Se o usuário existir, fazemos o oposto.

Agora que temos um entendimento sólido do que deve ser implementado, vamos explorar algumas opções, mas primeiro vamos configurar nosso projeto React,

O repositório do projeto contém um exemplo de aplicativo backend que usaremos para implementar o lado frontend (não entraremos nisso porque não é o foco principal aqui, mas o código foi intencionalmente mantido simples para que você não tenha dificuldades com ele )

Começamos criando as seguintes páginas e componentes:

  • Pagina inicial

  • Página de login

  • Editar página do usuário

  • Componente da barra de navegação

  • Componente UserDropdown

Uma aplicação React com múltiplas páginas necessita de navegação adequada, para isso podemos usar o react-router-dom para criar um contexto global de roteador de navegador e registrar diferentes rotas React.


yarn add react-router-dom

Sabemos que muitos leitores preferem seguir os tutoriais, então aqui está o modelo inicial para você ficar atualizado. Este branch inicial usa DaisyUI para componentes TailwindCSS JSX predefinidos. Inclui todos os componentes, páginas e o roteador já configurado. Se você está pensando em seguir este tutorial, construindo todo o fluxo de autenticação sozinho seguindo etapas simples, comece primeiro bifurcando o repositório. Depois de bifurcar o repositório, clone-o e inicie no start-herebranch:


git clone git@github.com:<your-username>/fullstack-resourcify.git

Depois de puxar o branch start-here:

  • Abra o projeto com seu editor de código preferido

  • Mude o diretório para frontend/

  • Instale dependências: fio

  • Inicie um servidor de desenvolvimento: yarn dev·

A visualização deve ser semelhante a esta:

Changing user name

O nome renderizado na barra de navegação – canto superior direito – é uma variável de estado definida no componente principal do aplicativo. A mesma variável é passada para a barra de navegação e para a página inicial. O formulário simples usado acima na verdade atualiza a variável de estado “nome” do componente EditPage.

As duas abordagens apresentadas abaixo entrarão em detalhes de implementação:

Primeira abordagem: useState

useState()

useState é um dos ganchos React mais comumente usados, ele permite criar e alterar o estado em um componente React Functional. O componente useState tem uma implementação muito simples e é fácil de usar: para criar um novo estado, você precisa chamar useState com o valor inicial do seu estado e o gancho useState retornará um array contendo duas variáveis: a primeira é o estado variável que você pode usar para referenciar seu estado, e a segunda, uma função que você usa para alterar o valor do estado: bastante simples.

Que tal vermos isso em ação? O nome renderizado na barra de navegação – canto superior direito – é uma variável de estado definida no componente principal do aplicativo. A mesma variável é passada para a barra de navegação e para a página inicial. O formulário simples usado acima na verdade atualiza a variável de estado “nome” do componente EditPage. O resultado final é o seguinte: useState é um gancho central que aceita um estado inicial como parâmetro e retorna duas variáveis ​​contendo dois valores, a variável de estado contendo o estado inicial e uma função setter para a mesma variável de estado.

Vamos analisar e ver como isso foi implementado em primeiro lugar.

  1. Criando a variável de estado “nome”:

./src/App.jsx


import { useState } from "react";

function App() {
const testValue = "CLA";
 //using the useState hook to create a state variable out of an initial value passed as an argument
 const [name, setName] = useState(testValue);
 console.log(`Rendering: ${name}`);

 return (...)};

Adereços

Os adereços são um dos blocos de construção básicos de um componente react, conceitualmente, se você pensar em um componente funcional React como uma função Javascript, então os adereços não são mais do que os parâmetros da função, combinar adereços e o gancho useState pode oferecer uma estrutura sólida para gerenciar o estado em um aplicativo React simples.

Os adereços React são passados ​​como atributos para componentes personalizados. Atributos passados ​​como props podem ser desestruturados do objeto props ao aceitá-lo como argumento, semelhante a este:

Passando adereços

<Routes>
  <Route path="/" element={<Home name={name} />} /> // Passing name as a prop to
  Home Component
  <Route
    path="/user"
    element={<EditUser name={name} setName={setName} />} // passing both name and setItem function as props to EditUser component
  />
  <Route path="/login" element={<Login />} />
</Routes>

Os adereços podem ser aceitos e usados ​​dentro de um componente funcional semelhante aos argumentos normais de uma função. É porque “nome” é passado como um suporte para o componente Home, que podemos renderizá-lo no mesmo componente. No exemplo a seguir, estamos aceitando o prop passado usando a sintaxe de desestruturação para extrair a propriedade name do objeto props.

Aceitando adereços

./src/pages/Home.jsx

... ...
export default function Home({name}) { //Destructuring the name property from the props object, another approach would be: Home(props.name)
 console.log("Rendering: Home");
 return (
   <div className="flex flex-col bg-white m-auto p-auto">
     <h1 className="flex py-5 lg:px-20 md:px-10 mx-5 font-bold text-2xl text-gray-800">
       Welcome {name}
     </h1>
... ...

Dica profissional

Abra o console do navegador e observe como todos os componentes que usam a propriedade “name” são renderizados novamente quando o estado muda. Ao manipular uma variável de estado, o React armazenará o próximo estado, renderizará seu componente novamente com os novos valores e atualizará a UI.

Components Re-rendering

useState Desvantagens

Perfuração de adereços

Perfuração de adereços é um termo para se referir a uma hierarquia de componentes onde um conjunto de componentes precisa de certos adereços fornecidos por um componente pai. Uma solução alternativa comum que desenvolvedores inexperientes geralmente usam é passar esses adereços por toda a cadeia de componentes, o problema com isso abordagem é que uma mudança em qualquer um desses adereços acionará a re-renderização de toda a cadeia de componentes, desacelerando efetivamente todo o aplicativo como resultado dessas renderizações desnecessárias, os componentes no meio da cadeia que não requerem esses adereços atuam como médiuns para transferir adereços.

Exemplo:

  • Uma variável de estado foi definida no componente principal do App usando o gancho useState()

  • Uma propriedade name foi passada para o componente Navbar

  • Mesma prop aceita na Navbar e passada como prop mais uma vez para o componente UserDropdown

  • UserDropdown é o último elemento filho que aceita a propriedade.

Prop-drilling

./src/App.jsx

... ...
function App() {
 const [name, setName] = useState(test);
 console.log("Rendering: App");

 return (
   <BrowserRouter>
     <div className="h-screen">
       <Navbar name={name} />

       <main className="px-4">
... ...

./src/components/Navbar.jsx

import React from "react";
import Logo from "../assets/cla.svg";
import { BiSearchAlt } from "react-icons/bi";
import { Link } from "react-router-dom";
import UserDropdown from "./UserDropdown";

export default function Navbar({ name }) {
  console.log("Rendering: Navbar");

  return (
    <>
      <div className="navbar bg-base-100 drop-shadow-sm">
        <div className="flex-1">
          <Link
            to="/"
            className="btn btn-ghost normal-case text-md md:text-xl px-2 gap-1"
          >
            <img src={Logo} className="h-6" alt="" />
            Resources
          </Link>
        </div>

        <UserDropdown name={name} />
      </div>
    </>
  );
}

Imagine como é difícil manter um aplicativo React com diferentes camadas de componentes, todos passando e renderizando o mesmo estado.

Complexidade crescente e qualidade do código

Com o uso de useState e props como único meio de gerenciamento de estado em uma aplicação react, a complexidade da base de código pode crescer rapidamente, tendo que gerenciar dezenas ou centenas de variáveis ​​de estado, que podem ser duplicatas umas das outras, espalhadas por diferentes arquivos e componentes podem ser bastante assustadores, qualquer alteração em uma determinada variável de estado exigirá uma consideração cuidadosa das dependências entre os componentes para evitar qualquer potencial renderização adicional em um aplicativo já lento.

Segunda abordagem: API de contexto

A API de Contexto é a tentativa do React de resolver as desvantagens de usar apenas props e useState para gerenciamento de estado, particularmente, a API de contexto vem como uma resposta ao problema mencionado anteriormente sobre a necessidade de passar props por toda a árvore de componentes. Com o uso do contexto, você pode definir um estado para os dados que você considera globais e acessar seu estado a partir de qualquer ponto na árvore de componentes: chega de perfuração de suporte.

É importante ressaltar que a API de contexto foi inicialmente concebida para resolver a questão do compartilhamento global de dados, dados como temas de UI, informações de autenticação que é nosso caso de uso, linguagens e afins), para outros tipos de dados que precisam ser compartilhados entre mais de um componente, mas não é necessariamente global para todo o aplicativo, o contexto pode não ser a melhor opção, dependendo do caso de uso, você pode considerar outras técnicas, como composição de componentes que está fora do escopo deste artigo.

Mudando para o contexto

Criando o contexto Auth

createContext é simples, ele cria uma nova variável de contexto e aceita um único parâmetro opcional: o valor padrão da variável de contexto.

Vamos ver isso em ação, primeiro crie uma nova pasta “contextos” e um novo arquivo dentro dela “Auth.jsx”. Para criar um novo contexto, precisamos invocar a função createContext(), atribuir o valor retornado a uma nova variável Auth que será exportada a seguir:

./src/contexts/Auth.jsx

import { createContext } from "react";

export const Auth = createContext();

Forneça o contexto de autenticação

Agora precisamos expor a variável de contexto "Auth" que criamos anteriormente, para conseguir isso usamos o provedor de contexto, o provedor de contexto é um componente que possui uma propriedade "valor", podemos usar esta propriedade para compartilhar um valor - nome – em toda a árvore de componentes, ao agrupar a árvore de componentes com o componente provedor, tornamos esse valor acessível de qualquer lugar dentro da árvore de componentes, sem a necessidade de passar essa propriedade para cada componente filho individualmente.

Em nosso exemplo de autenticação, precisamos de uma variável para armazenar o objeto de usuário e algum tipo de setter para manipular essa variável de estado. Este é um caso de uso perfeito para useState. Ao usar o Context, você precisa ter certeza de que está definindo os dados que deseja fornecer e passando esses dados - usuário em nosso exemplo - para toda a árvore de componentes aninhada dentro, então defina uma nova variável de estado usuário dentro do componente provedor e finalmente passamos user e setUser dentro de um array como o valor que o provedor irá expor para a árvore de componentes:

./src/contexts/Auth.jsx

import { createContext, useState } from "react";

export const Auth = createContext();

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  return <Auth.Provider value={[user, setUser]}>{children}</Auth.Provider>;
};

Outra coisa que podemos fazer é mover nossa variável de estado “nome” do componente principal do aplicativo para o contexto Auth e expô-la a componentes aninhados:

import { createContext, useState } from "react";

export const Auth = createContext();

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [name, setName] = useState("CLA");
  return <Auth.Provider value={[name, setName]}>{children}</Auth.Provider>;
};

Agora tudo o que resta é aninhar nosso aplicativo no mesmo componente AuthProvider que acabamos de exportar.

./src/main.jsx:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { AuthProvider } from "./contexts/Auth";

import "./index.css";

ReactDOM.createRoot(document.getElementById("root")).render(
  <AuthProvider>
    <App />
  </AuthProvider>
);

Como estamos renderizando a propriedade children dentro de Auth.Provider, todos os elementos aninhados dentro do componente AuthProvider agora podem consumir a propriedade value que passamos para Auth.Provider. Pode parecer confuso, mas depois de experimentar, tente fornecer e consumir um estado global - Contexto. Afinal, isso só fez sentido para mim depois de experimentar a API Context.

Consumindo o contexto de autenticação

A etapa final é simples, usamos o gancho de contexto "useContext" para acessar o valor que o provedor de contexto de "Auth" está fornecendo, que no nosso caso é o array contendo user e setUser, no código a seguir, podemos use useContext para consumir o contexto Auth dentro da barra de navegação. Isso só é possível porque Navbar está aninhado dentro do componente App, e como AuthProvider envolve o componente App, a propriedade value pode ser consumida usando apenas o gancho useContext. Outra ferramenta incrível que o React fornece pronta para uso para gerenciar quaisquer dados que possam ser acessados ​​globalmente e também manipulados por qualquer componente do consumidor.

./src/components/Navbar.jsx

export default function Navbar() {
 const [name, setName] = useContext(Auth);
 console.log(name)

 return (...)};

Observe como não aceitamos mais nenhum adereço no componente funcional Navbar(). Em vez disso, estamos usando useContext(Auth) para consumir o contexto Auth, capturando name e setName. Isso também significa que não precisamos mais passar o suporte para a Navbar:

./src/App.jsx

// ... //
return (
   <BrowserRouter>
     <div className="h-screen">
       <Navbar/> // no need to pass prop anymore
// ... //

Atualizando o contexto de autenticação

Também podemos usar a função setName fornecida para manipular a variável de estado “nome”:

./src/pages/EditUser.jsx

export default function EditUser() { // no need to accept props anymore
 const [name, setName] = useContext(Auth); // grabbing the name and setName variables from Auth context
 console.log("Rendering: EditUser");

Apresentando o gancho useReducer()

No exemplo anterior, usamos a API de contexto para gerenciar e compartilhar nosso estado na árvore de componentes. Você deve ter notado que ainda estamos usando useState como base de nossa lógica, e isso está ok, pois nosso estado ainda é um objeto simples neste ponto, mas se quisermos expandir as capacidades do nosso fluxo de autenticação, definitivamente precisaremos armazenar mais do que apenas o e-mail do usuário conectado no momento, e é aqui que voltamos às limitações que entramos anteriormente em relação a felizmente, o React resolve esse problema fornecendo uma alternativa ao useState para gerenciar estados complexos: insira useReducer.

useReducer pode ser pensado como uma versão generalizada de useState, leva dois parâmetros: uma função redutora e um estado inicial.

Nada de interessante a ser observado para o estado inicial, a mágica acontece dentro da função redutora: ela verifica o tipo de ação que ocorreu e dependendo dessa ação, o redutor determinará quais atualizações aplicar ao estado e retornará seu novo valor .

Olhando para o código abaixo, a função redutora tem dois tipos de ações possíveis:

  • "LOGIN": nesse caso o estado do usuário será atualizado com as novas credenciais do usuário fornecidas dentro do payload da ação.

  • "LOGOUT": neste caso o estado do usuário será removido do armazenamento local e redefinido como nulo.

É importante observar que o objeto de ação contém um campo de tipo que determina qual lógica aplicar e um campo de carga útil opcional para fornecer os dados necessários para aplicar essa lógica.

Finalmente, o gancho useReducer retorna o estado atual e uma função de expedição que usamos para passar uma ação para o redutor.

Para o resto da lógica, é idêntica ao exemplo anterior:

./src/contexts/Auth.jsx

import { createContext, useEffect, useReducer, useState } from "react";

export const Auth = createContext();
import { createContext, useReducer } from "react";

export const Auth = createContext();
const reducer = (state, action) => {
  switch (action.type) {
    case "LOGIN":
      return { user: action.payload };

    case "LOGOUT":
      localStorage.removeItem("user");
      return { user: null };

    default:
      break;
  }
};

export const AuthProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, {
    user: null,
  });

  return (
    <Auth.Provider value={{ ...state, dispatch }}>{children}</Auth.Provider>
  );
};

Despachando ações em vez de usar a função setter setState – por exemplo: setName –

Como acabamos de mencionar, usamos a função de expedição para passar uma ação para o redutor, no código a seguir, iniciamos uma ação LOGIN e fornecemos o e-mail do usuário como carga útil, agora o estado do usuário será atualizado e esta mudança será acionada uma nova renderização de todos os componentes inscritos no estado do usuário. É importante ressaltar que uma nova renderização só será acionada se ocorrer uma mudança real no estado, nenhuma nova renderização se o redutor retornar o mesmo estado anterior.

export default function Login() {
 const { dispatch } = useContext(Auth);


 const handleLogin = async (e) => {
   // Updating the global Auth context
   dispatch({ type: "LOGIN", payload: {email: email.current.value} });
 };

 return (...)};

Login de usuário

JWT Local storage

Dica profissional

Observe como o objeto de usuário que recebemos após um login bem-sucedido agora é armazenado no localStorage.

Gancho personalizado para login

Agora que temos um bom domínio do useReducer, podemos encapsular ainda mais nossa lógica de login e logout em seus próprios ganchos personalizados separados. Por meio de uma única chamada para o gancho Login, podemos lidar com uma chamada de API para a rota de login, recuperar o novo usuário credenciais e armazená-las no armazenamento local, enviar uma chamada LOGIN para atualizar o estado do usuário, ao mesmo tempo em que lida com o tratamento de erros:

./src/hooks/useLogin.jsx

import { useContext, useState } from "react";
import { Auth } from "../contexts/Auth";

export const useLogin = () => {
  const [error, setError] = useState(null);
  const [isLoading, setIsLoading] = useState(false);

  const { dispatch } = useContext(Auth);

  const login = async (email, password) => {
    setIsLoading(true);
    setError(null);

    try {
      const response = await fetch("/api/users/login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, password }),
      });

      const json = await response.json();

      if (json.name === "Error") {
        setError(json.message);
        setIsLoading(false);
      }

      if (!response.ok) {
        setIsLoading(false);
        setError(json);
      }

      if (response.ok) {
        // Save the user and token in the localstorage
        localStorage.setItem("user", JSON.stringify(json));

        // Updating the global Auth context
        dispatch({ type: "LOGIN", payload: json });

        setIsLoading(false);
      }
    } catch (error) {
      console.log(error);
    }
  };

  return { error, isLoading, login };
};

Nota: para usuários mais avançados do React entre os leitores, você deve estar se perguntando por que não usamos o recurso de inicialização lenta de useReducer para recuperar as credenciais do usuário, useReducer aceita um terceiro parâmetro opcional chamado função init, esta função é usada no caso precisamos aplicar alguma lógica antes de podermos obter o valor inicial do estado, a razão pela qual não optamos por isso é uma simples questão de separação de interesses, o código desta forma é mais simples de entender e como resultado mais simples de manter .

Página de login

Esta é a aparência da parte superior de nossa página de login depois de usar o gancho useLogin() para extrair a função de login e invocar a função de login com credenciais enviadas por um usuário:

// ... ... //
export default function Login() {
 const { login, isLoading, error } = useLogin();
 console.log("Rendering: Login");
 const email = createRef(null);
 const password = createRef(null);

 const handleLogin = async (e) => {
   await login(email.current.value, password.current.value);
 };
 return (...)
// ... ... //

Também estamos desabilitando a função Enviar quando o usuário envia o formulário:

<button
  onClick={handleLogin}
  disabled={isLoading}
  className="btn btn-square w-full bg-gray-100 text-gray-600 hover:bg-gray-300 border-none"
>
  {isLoading && "A moment please!"}
  {!isLoading && "Login"}
</button>

E renderizando quaisquer erros de autenticação que recebemos do nosso back-end:

{
  error && <span className="text-red-500 p-2">{error.message}</span>;
}

Rendering Errors

Mantendo o estado do usuário

Você deve estar se perguntando por que precisamos armazenar o objeto de usuário no localStorage. Simplificando, queremos manter o usuário conectado enquanto o token não expirar. Usar localStorage é uma ótima maneira de armazenar bits de JSON, assim como em nosso exemplo. Observe como o estado é eliminado se você atualizar a página após o login. Isso pode ser resolvido facilmente usando um gancho useEffect para verificar se temos um objeto de usuário armazenado em localStorage, se houver, fazemos login no usuário automaticamente:

// ... ... //
export const AuthProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, {
    user: null,
  });

  useEffect(() => {
    const user = JSON.parse(localStorage.getItem("user"));

    if (user) {
      return dispatch({ type: "LOGIN", payload: user });
    }
  }, []);

  return (
    <Auth.Provider value={{ ...state, dispatch }}>{children}</Auth.Provider>
  );
};

Ganchos personalizados para logout

A mesma coisa se aplica ao gancho Logout, aqui estamos despachando uma ação LOGOUT para remover as credenciais do usuário atual do estado e do armazenamento local:

./src/hooks/useLogout.jsx

import { useContext } from "react";
import { Auth } from "../contexts/Auth";

export const useLogout = () => {
  const { dispatch } = useContext(Auth);

  const logout = () => {
    // delete user from the localstorage
    localStorage.removeItem("user");
    // Wipe out the Auth context (user:null) / dipatch 'LOGOUT'
    dispatch({ type: "LOGOUT" });
  };

  return { logout };
};

Sair do usuário

Para desconectar um usuário, vamos adicionar um evento de clique ao botão Logout encontrado em UserDropdown.jsx e tratá-lo de acordo:

./src/components/UserDropdown.jsx

// Extracting the logout function from useLogout() and handling the click event listener //
export default function UserDropdown() {
 const { user } = useContext(Auth);
 const { logout } = useLogout();
 console.log("Rendering: UserDropdown");

 const handleLogout = () => {
   logout();
 };
// ... ... //

// Adding a click event listener to logout button //
<li>
   <button onClick={handleLogout}>Logout</button>
</li>
// ... ... //

User Logout

Protegendo Rotas de Reação

A etapa final na implementação de nosso aplicativo é aproveitar o estado global do usuário para controlar a navegação do usuário, um rápido lembrete sobre qual comportamento devemos alcançar: inicialmente o usuário é saudado pela página de login, a partir desse ponto o usuário pode acessar apenas a página inicial após um login bem-sucedido, da mesma forma, o usuário será redirecionado para a página de login após o logout.

Conseguimos isso com a ajuda da biblioteca react-router-dom definindo 2 rotas: "/" e "/login", controlamos qual componente renderizar em cada rota usando o estado de autenticação global, a avaliação de autenticação como null representa um usuário não autenticado e vice-versa:

./src/App.jsx

import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import "./App.css";
import Navbar from "./components/Navbar";
import Home from "./pages/Home";
import Login from "./pages/Login";
import { useContext } from "react";
import { Auth } from "./contexts/Auth";

function App() {
  const { user } = useContext(Auth);
  return (
    <BrowserRouter>
      <div className="h-screen">
        <Navbar />
        <main className="px-4">
          <Routes>
            <Route
              path="/"
              element={user ? <Home /> : <Navigate to="/login" />}
            />
            <Route
              path="/login"
              element={!user ? <Login /> : <Navigate to="/" />}
            />
          </Routes>
        </main>
      </div>
    </BrowserRouter>
  );
}

export default App;

Diagrama de recapitulação

Diagram

Embrulhar

Neste artigo tentamos abordar o caso de uso simples, mas muito comum, de implementação de gerenciamento de estado para um fluxo de trabalho de autenticação, passando pelas diferentes abordagens, a lógica por trás de cada uma e suas vantagens e desvantagens. O gerenciamento de estado em estruturas do lado do cliente e no React em particular é um dos tópicos mais comentados na comunidade frontend, simplesmente porque pode melhorar ou prejudicar o desempenho e a escalabilidade do seu aplicativo. A enorme quantidade de diferentes técnicas, padrões, bibliotecas e ferramentas que tentam resolver esta questão de gestão de estado é impressionante, nosso objetivo era dar-lhe uma compreensão sólida das práticas para que você possa implementá-las em sua própria aplicação, para aplicações mais avançadas. técnicas e padrões de gerenciamento de estado em aplicações mais complexas, confira nosso próximo artigo onde abordaremos redux para gerenciamento de estado escalável no React.

Em breve

ContextAPI vs Kit de ferramentas Redux

Redux teve muitos anos de adoção na comunidade antes do kit de ferramentas Redux; na verdade, o RTK foi introduzido como um modelo inicial para inicializar o gerenciamento de estado redux em novos aplicativos (seu nome inicial era “redux-starter-kit” em outubro de 2019), embora hoje, há um consenso geral entre os mantenedores do Redux e a comunidade de que o kit de ferramentas Redux é a maneira válida de trabalhar com redux.RTX abstrai muito da lógica do Redux, o que o torna mais fácil de usar, com muito menos verboso, e incentiva os desenvolvedores a siga as melhores práticas, uma diferença principal entre os dois é que o Redux foi construído para não ter opinião, fornecendo uma API mínima e esperando que os desenvolvedores façam a maior parte do trabalho pesado escrevendo suas próprias bibliotecas para tarefas comuns e lidando com a estrutura do código, isso resultou em tempo de desenvolvimento lento e código confuso, o kit de ferramentas Redux foi adicionado como uma camada extra de abstração evitando que os desenvolvedores caíssem em suas armadilhas comuns, consulte a documentação oficial para obter mais informações e o raciocínio de seus mantenedores aqui.


Career Services background pattern

Serviços de carreira

Contact Section background image

Vamos manter-nos em contacto

Code Labs Academy © 2025 Todos os direitos reservados.