Olá, estudante da Alura! Meu nome é Vinicios Neves, sou instrutor e estou aqui para acompanhá-lo(a) neste curso.
Audiodescrição: Vinicios é um homem branco, com cabelo escuro e barba escura. Ele veste uma roupa preta e está em um ambiente iluminado pelas cores rosa e azul.
Olá! Sejam bem-vindos a mais um curso de React na plataforma da Alura. Neste curso, vamos explorar o gerenciamento de estado global em aplicações React. Introduziremos a biblioteca Redux, que é amplamente utilizada para esse propósito e se baseia na arquitetura Flux. Analisaremos dados históricos, princípios do Redux e da arquitetura Flux, e implementaremos tudo isso em nossa aplicação.
A aplicação que utilizaremos é o Study Planner
, um gerenciador de plano de estudos personalizado. Este projeto permite visualizar o total de cursos e tarefas, a porcentagem de tarefas concluídas, e possibilita marcar, desmarcar, editar e adicionar descrições opcionais. Por exemplo, podemos incluir uma nota como "deve ser com o curso do instrutor Vinicios" e salvar essas informações em tempo real. Este projeto é uma excelente adição ao portfólio e pode ser compartilhado com outras pessoas.
Exploraremos diversos recursos do React e do Redux, compreendendo como o Redux funciona em comparação com a Context API. Analisaremos dados comparativos para entender as melhorias possíveis. Este curso requer algum conhecimento prévio de React, especialmente no uso de Hooks, e de JavaScript. Utilizaremos estilização com CSS, mas não se preocupem, pois guiaremos vocês passo a passo nesse processo de aprendizado sobre gerenciamento global de estados em aplicações React, com Redux e Context API.
No final do curso, aprenderemos a debugar via navegador com o Redux DevTools e a realizar persistência com o localStorage
. Haverá um bônus com uma função especial para melhorar ainda mais a performance da aplicação. Vamos lá? Espero vocês na sequência do curso!
Vamos iniciar nossa jornada de aprendizado sobre estados globais em uma aplicação React. No dia a dia, passamos cerca de 80% do tempo lidando com alterações globais em estados em uma aplicação, mais do que desenvolvendo uma tela ou uma funcionalidade. Essa é uma parte crucial em uma aplicação moderna que utiliza React e necessita de gerenciamento de estado global.
Vamos entender como isso funciona. Em um cenário comum, aplicações React, no formato single page application (SPA), que é o padrão do framework React, juntamente com o Vite, por exemplo, costumam passar props via atribuição de um componente. Temos o gerenciamento de estado local nesse componente. Imagine que estamos desenvolvendo um filtro para verificar quais tarefas já foram completadas. Para passar essa informação de filtragem, é necessário transferi-la de um componente pai para um componente filho, e esse filho pode precisar passá-la para outro componente aninhado. Esse é o famoso prop drilling, um problema comum quando o gerenciamento de estado em aplicações React é feito de forma inadequada.
Para ilustrar o problema do prop drilling, vejamos um exemplo de código sem estado global:
// Sem estado global - prop drilling
function App() {
const [usuario, setUsuario] = useState('João');
return <Dashboard usuario={usuario} />;
}
function Dashboard({ usuario }) {
return <Sidebar usuario={usuario} />;
}
function Sidebar({ usuario }) {
return <UserInfo usuario={usuario} />;
}
Neste exemplo, a informação do usuário é passada de componente em componente, o que pode se tornar complicado à medida que a aplicação cresce.
Existem duas formas de gerenciamento de estado: o estado local, que é o estado interno de um componente ou página, e o estado global, compartilhado por toda a aplicação React. O estado local refere-se a dados que apenas um componente precisa. Um exemplo clássico é um campo de formulário. Mesmo que o formulário esteja inserido em um contexto, as informações que ele necessita são exclusivas para ele, e o que digitamos no formulário não interessa ao restante da aplicação. Isso ilustra bem a diferença entre estado local e global.
Por outro lado, o estado global envolve dados que vários componentes utilizam. Em um projeto sem ferramentas específicas, essas informações são passadas por meio de atribuição de propriedades, caracterizando o prop drilling. Exemplos de dados compartilhados por vários componentes incluem informações de um usuário logado em um marketplace ou tela de carrinho, o tema de cores da aplicação e informações do carrinho, como valor, quantidade e forma de pagamento. Tudo isso faz parte do mesmo ecossistema e contexto, necessitando de compartilhamento.
Uma questão importante no gerenciamento de estado é que, ao utilizar o estado local e o prop drilling, se temos uma função App
passando informações de usuário, como const [usuario, setUsuario]
, para todos os componentes que precisam dessa informação, sem um estado global, é necessário atribuir a cada um deles a informação do usuário, caracterizando o prop drilling.
Já utilizando um estado global, podemos acessar diretamente o estado sem passar props por todos os componentes intermediários. Veja como isso é feito com o uso de um estado global:
// Com estado global - acesso direto
function UserInfo() {
const usuario = useSelector(state => state.usuario);
return <p>Olá, {usuario}</p>;
}
Neste exemplo, o componente UserInfo
acessa diretamente o estado global usando o hook useSelector
, eliminando o problema do prop drilling.
Para acessar o estado global, temos duas formas principais, que são as mais utilizadas no mercado atualmente. São as que também utilizamos no nosso dia a dia, pois são as mais fáceis e têm mais material e suporte da comunidade. A primeira delas é a Context API do próprio React, enquanto a segunda é o Redux. Talvez já tenhamos ouvido falar sobre o Redux em algum momento, então chegou a hora de aprofundarmos nesses conceitos e entendermos mais sobre o funcionamento e o que é cada um desses gerenciamentos de estado global.
Em uma breve comparação, de um lado, a Context API já vem com o React, configurada e pronta para uso, enquanto o Redux é uma biblioteca externa que necessita de instalação e configuração dentro do nosso projeto. Não que a Context API não exija configuração dos nossos seletores, do nosso Context e tudo mais, porém, com o Redux, temos algumas configurações adicionais a fazer. Nada muito difícil, mas há mais código a ser implementado.
A Context API é excelente para estados simples, pela simplicidade com que funciona e pela forma como é implementada. Provavelmente já a utilizamos até este momento em nossa jornada nos cursos. Caso contrário, não se preocupe, pois veremos no código como está implementada e revisaremos alguns pontos rapidamente quando formos para o código. A parte central dela é mais simples de funcionar, e sua lógica é mais direta. Por isso, é excelente para estados simples, como gerenciamento de tema claro e escuro, entre outros. Não que não possamos utilizá-la para outras coisas, mas em sua simplicidade e estrutura inicial, ela já é excelente para questões mais simples. Enquanto isso, o Redux será mais performático em estados complexos.
Para ilustrar o uso da Context API, vejamos um exemplo de implementação:
// Context API - simples mas limitado
const TemaContext = createContext();
function App() {
const [tema, setTema] = useState('claro');
return (
<TemaContext.Provider value={{ tema, setTema }}>
<MeuApp />
</TemaContext.Provider>
);
}
Neste exemplo, criamos um contexto de tema utilizando createContext
e fornecemos o estado do tema para todos os componentes dentro do TemaContext.Provider
.
Isso ocorre porque o Redux dispõe de várias ferramentas internas que funcionam nos bastidores. Utilizaremos o Redux de forma moderna, através do Toolkit do RTK, o famoso Redux Toolkit, que é a forma mais moderna e utilizada atualmente com o Redux. Devido a isso, o Redux requer mais código para inicialização, ou seja, essa configuração inicial. Enquanto a Context API, por já vir como padrão no React, sendo uma extensão de funções do React, tem uma implementação inicial mais rápida.
Para configurar o Redux, utilizamos o configureStore
, que nos permite definir os reducers e outras configurações:
// Redux - mais poder e controle
const store = configureStore({
reducer: {
tema: temaReducer
}
});
Neste exemplo, configuramos a store do Redux com um reducer para o tema, permitindo um controle mais detalhado sobre o estado global.
Temos um exemplo simples de um contexto de tema. Utilizamos o createContext
, que é uma função do próprio React. Definimos const TemaContext = createContext
. Dentro da função App
, passamos o tema utilizando o useState
, e esse useState
vai dentro do TemaContext.Provider
, que é o provedor de contexto para todos os componentes e páginas alinhados dentro do TemaContext.Provider
. A partir desse alinhamento com o Provider, que utilizamos normalmente em aplicações ao usar useContext
, todos os componentes abaixo dessa hierarquia têm acesso às informações do estado global.
Enquanto no Redux, temos o configureStore
, onde discutiremos mais adiante sobre os pilares do Redux. Basicamente, ele funciona em cima de Reducer e Slices, que veremos mais detalhadamente na documentação do Redux. Temos mais poder e controle porque a forma como configuramos é mais customizada e permite definir parâmetros e configurações adicionais que o contexto não oferece. O exemplo completo do Redux é extenso, com muitos arquivos a serem modificados, e isso será feito na prática.
Antes de entrarmos na implementação e instalação, vamos entender mais sobre os pilares que sustentam o Redux. Também abordaremos um pouco da história dele, como surgiu e o que trouxe de diferencial dentro do universo do React. Por muitos anos, foi a ferramenta mais utilizada, até o surgimento do Context. Entenderemos isso na documentação do Redux.
Esperamos continuar explorando esse universo de gerenciamento de estado global dentro de aplicações React.
Vamos aprofundar nosso entendimento sobre o gerenciamento de estado global em aplicações com React. Atualmente, estamos trabalhando no projeto Study Planner, onde vamos expandir as extensões de estado global e melhorar algumas funcionalidades necessárias.
No dia a dia, frequentemente nos deparamos com aplicações que crescem rapidamente, incorporando novas funcionalidades que exigem atenção especial. Vamos abrir nosso editor de texto e navegar até a pasta "src" e, em seguida, até a pasta "context". Dentro dessa pasta, encontramos dois contextos que nossa aplicação utiliza. Um deles é o contexto de tema, responsável pelo gerenciamento das cores da aplicação. Ao clicar no ícone de sol no cabeçalho do plano de estudos, o tema alterna entre claro e escuro. Toda essa lógica está implementada no arquivo themeContext.jsx
, entre as linhas 12 e 26, onde ocorre a diferenciação entre os temas claro e escuro.
Para entender melhor como isso é feito, vamos dar uma olhada no código que implementa essa funcionalidade:
import { createContext, useContext, useState } from 'react'
const ThemeContext = createContext()
export function ThemeProvider({ children }) {
const [isDarkTheme, setIsDarkTheme] = useState(true)
const toggleTheme = () => {
setIsDarkTheme(!isDarkTheme)
}
const theme = {
isDark: isDarkTheme,
background: isDarkTheme
? 'url("/src/assets/bg-dark.png")'
: 'url("/src/assets/bg-light.png")',
backgroundColor: isDarkTheme ? '#1F2937' : '#f3f4f6',
cardBg: isDarkTheme ? 'bg-card-dark' : 'bg-white',
textPrimary: isDarkTheme ? 'text-white' : 'text-gray-800',
textSecondary: isDarkTheme ? 'text-gray-300' : 'text-gray-700',
textMuted: isDarkTheme ? 'text-gray-400' : 'text-gray-600',
inputBg: isDarkTheme ? 'bg-gray-700' : 'bg-white',
inputBorder: isDarkTheme ? 'border-gray-600' : 'border-gray-400',
dividerColor: isDarkTheme ? 'border-gray-600' : 'border-gray-300',
dividerPurple: isDarkTheme ? 'border-purple-header' : 'border-purple-600'
}
const value = {
theme,
toggleTheme
}
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
)
}
export function useTheme() {
const context = useContext(ThemeContext)
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider')
}
return context
}
O uso de context é adequado para questões simples como essa, onde temos constantes e operadores ternários alternando entre cores. Mesmo que houvesse mais lógica envolvida, o context ainda seria eficiente. Já desenvolvemos aplicativos móveis utilizando apenas a API do useContext
, sem necessidade de ferramentas adicionais como Redux, especialmente em aplicativos mais simples.
No entanto, ao analisarmos o TaskContext
, que gerencia tarefas no projeto, percebemos que precisamos desenvolver uma parte analítica. Precisamos de dados sobre a quantidade de tarefas concluídas no último mês ou semana, além das pendentes. Essas regras de negócio exigem cuidado e centralização das informações de analíticas e filtros em um local acessível por toda a aplicação. Embora possamos usar o context, há riscos de comprometer informações ao realizar cálculos no mesmo local.
Vamos ver como o TaskContext
é implementado:
import { createContext, useContext, useState } from 'react'
const TaskContext = createContext()
export function TaskProvider({ children }) {
const [tasks, setTasks] = useState([])
const addTask = (newTask) => {
const taskWithId = {
...newTask,
id: Date.now(),
completed: false
}
setTasks((prevTasks) => [...prevTasks, taskWithId])
}
const toggleTaskComplete = (taskId) => {
setTasks(prevTasks =>
prevTasks.map(task =>
task.id === taskId ? { ...task, completed: !task.completed }
: task
)
)
}
const editTask = (taskId, updatedTask) => {
setTasks(prevTasks =>
prevTasks.map(task =>
task.id === taskId ? { ...task, ...updatedTask }
: task
)
)
}
const deleteTask = (taskId) => {
setTasks(prevTasks => prevTasks.filter(task => task.id !== taskId))
}
const getPendingTasks = () => tasks.filter(task => !task.completed)
const getCompletedTasks = () => tasks.filter(task => task.completed)
const value = {
tasks,
addTask,
toggleTaskComplete,
editTask,
deleteTask,
getPendingTasks,
getCompletedTasks,
}
return (
<TaskContext.Provider value={value}>
{children}
</TaskContext.Provider>
)
}
export function useTasks() {
const context = useContext(TaskContext)
if (context === undefined) {
throw new Error('useTasks must be used within a TaskProvider')
}
return context
}
Implementar o Redux, utilizando reducers e compreendendo o funcionamento de slices, nos permitirá uma abordagem mais segura. Embora exija mais configuração inicial, a aplicação se torna mais robusta contra problemas de dados, pois o Redux adota o princípio de uma única fonte de verdade. Isso aumenta a confiabilidade das informações, já que o Redux se baseia em uma única fonte de dados para toda a aplicação.
Para acessar a documentação do Redux, que está em inglês, podemos utilizar o Chrome para traduzir a página. Na documentação, a motivação para o uso do Redux destaca a necessidade de gerenciar mais estados do que nunca, o que se aplica ao nosso caso no Study Planner. À medida que adicionamos mais informações, perdemos controle sobre o estado, como mencionado na documentação.
Esses pontos da documentação do Redux se alinham com nossas necessidades no Study Planner. Muitas vezes, começamos uma aplicação pensando que será simples ou como um MVP, mas acabamos recebendo demandas inesperadas que exigem uma revisão completa do gerenciamento global da aplicação. Novos requisitos estão se tornando comuns no desenvolvimento de produtos front-end, e precisamos estar preparados para essas mudanças.
A adoção de analytics tem se tornado uma tendência significativa nas aplicações atuais, e é exatamente isso que vamos implementar em nosso Study Planner. Precisamos ter um controle maior sobre nossos estados, e para isso, utilizamos dois conceitos fundamentais: mutação e assincronicidade, que são as bases do trabalho com Redux.
Esses conceitos se misturam, pois, ao transformar nosso código, as alterações não ocorrem de forma sequencial, mas sim de maneira assíncrona. Isso significa que, enquanto atualizamos um estado, outro pode não ser atualizado simultaneamente. O Redux busca tornar as mutações de estados previsíveis, mesmo quando lidamos com assincronicidade no gerenciamento de estado.
A sincronicidade é crítica, pois muitos produtos são desenvolvidos sem considerar esse aspecto. Por exemplo, ao clicar em um botão para atualizar a quantidade no carrinho, é necessário que o produto seja descontado do estoque. Isso deve ocorrer de forma assíncrona, garantindo que, se outra pessoa tentar comprar o mesmo produto, a informação sobre a disponibilidade esteja atualizada e confiável.
O Redux foi criado para tornar as mutações de dados previsíveis, mesmo com assincronicidade, utilizando três princípios fundamentais. O primeiro é a fonte única de verdade, onde o estado global do aplicativo é armazenado em uma árvore de objetos de um único armazenamento. Isso garante que, ao consultar um dado, ele sempre virá do mesmo local, sem interferência externa, assegurando a confiabilidade da informação.
O segundo princípio é que o estado é único. Para alterar o estado, é necessário emitir uma ação que descreve exatamente o que ocorrerá. Assim, as alterações são feitas através de ações descritivas, garantindo que o estado permaneça único e refletido na única fonte de verdade.
O terceiro princípio, considerado o mais importante, é que as alterações são feitas através de funções, os reducers. Essas funções são puras, ou seja, dado o mesmo valor de entrada, o retorno será sempre o mesmo. Com esses três pilares, o Redux assegura que a aplicação siga um alto padrão e que o estado seja confiável, permitindo a aplicação de complexidade lógica de regras de negócio.
Um pouco de história: o Redux surgiu da necessidade do Angular em 2011 por um gerenciamento de estado global. Em 2014, o Flux foi lançado em parceria com o Facebook, e em 2015, Dan Abramov, conhecido na comunidade React, desenvolveu o Redux, inspirado na arquitetura Flux. Com o surgimento dos Hooks em 2017 e a Context API no React, em 2019 foi lançado o Toolkit, a forma mais moderna de utilizar o Redux, simplificando o gerenciamento de estado global.
Anteriormente, o Redux era complexo e verboso, exigindo várias bibliotecas adicionais, como Redux Thunk e Redux Saga. Hoje, o Toolkit resolve esses problemas de maneira mais simples. Compreendemos o contexto geral do Redux, seus pilares, motivação e história. Vamos agora explorar o código e entender como podemos aplicar essas práticas no nosso Study Planner para migrar para os próximos estados globais da aplicação.
Nos vemos na sequência, estudante.
O curso React: gerenciando estados com redux e context API possui 207 minutos de vídeos, em um total de 43 atividades. Gostou? Conheça nossos outros cursos de React em Front-end, ou leia nossos artigos de Front-end.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Matricule-se no plano PLUS e garanta:
Mobile, Programação, Front-end, DevOps, UX & Design, Marketing Digital, Data Science, Inovação & Gestão, Inteligência Artificial
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você participa de eventos exclusivos, pode tirar dúvidas em estudos colaborativos e ainda conta com mentorias em grupo com especialistas de diversas áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Para estudantes ultra comprometidos atingirem seu objetivo mais rápido.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.
Conecte-se ao mercado com mentoria personalizada, vagas exclusivas e networking estratégico que impulsionam sua carreira tech para o próximo nível.