Como Configurar Redux com Redux-Saga e Redux-Persist em Projetos React: Guia Prático

Configurar Redux e Redux Saga em um projeto React pode parecer desafiador, mas traz muitos benefícios na gestão do estado da aplicação. Passo a passo, é possível criar um store eficiente e integrar sagas que lidam com efeitos colaterais de forma organizada. Esta abordagem não apenas melhora a estrutura do código, mas também facilita a manutenção e escalabilidade do projeto.

Neste artigo, o autor mostrará como definir o ambiente, criar o store do Redux e implementar as sagas necessárias. Além disso, ele apresentará uma integração prática com o Redux Persist, que garante a persistência do estado mesmo após recarregar a página. Assim, os desenvolvedores poderão entender como otimizar sua aplicação com menos esforço e mais clareza.

Para quem busca simplificar a gestão de estado em aplicações React, a configuração correta de Redux e Redux Saga é fundamental. Acompanhe o desenvolvimento do exemplo e veja na prática como essas ferramentas podem potencializar um projeto de forma eficiente.

Configurando o Ambiente de Desenvolvimento

Para iniciar o desenvolvimento com React, Redux e Redux Saga, é essencial preparar o ambiente adequadamente. Isso inclui a instalação das dependências necessárias e a criação de uma estrutura inicial de diretório que facilite a organização do projeto.

Instalação de Dependências

Primeiramente, é necessário garantir que o Node.js e o npm estejam instalados no sistema. Com esses pré-requisitos atendidos, o desenvolvedor pode criar um novo projeto React usando o comando:

npx create-react-app nome-do-projeto
Terminal

Após a configuração inicial do projeto, deve-se instalar as bibliotecas essenciais:

npm install redux react-redux redux-saga redux-persist
Terminal

Essas bibliotecas fornecem a funcionalidade básica do Redux e do Redux Saga, além do suporte para persistência de estado. A instalação do redux-persist é opcional, mas recomendado para manter o estado do Redux entre as recargas da aplicação.

Estrutura Inicial do Diretório de Projeto

Com as dependências instaladas, o próximo passo é estabelecer uma estrutura clara de diretórios. Uma organização lógica oferece maior manutenção e escalabilidade. Uma configuração sugerida pode incluir:

src/
├── components/
├── pages/
├── redux/
   ├── actions/
   ├── reducers/
   └── sagas/
└── App.js
Terminal
  • components/: Armazena todos os componentes reutilizáveis da aplicação.
  • pages/: Contém as páginas principais, onde os componentes são compostos.
  • redux/: Agrupa todos os arquivos relacionados ao Redux, incluindo ações, redutores e sagas.

Essa estrutura ajuda no gerenciamento do projeto conforme ele cresce, facilitando a localização e modificações necessárias à medida que novas funcionalidades são adicionadas.

Criação do Store do Redux

A criação do store do Redux envolve a definição de reducers, a configuração de middlewares e a integração com o React. Cada um desses aspectos é fundamental para garantir que a aplicação funcione corretamente e de maneira eficiente.

Entendendo os Reducers

Os reducers são funções puras que gerenciam o estado de uma aplicação. Cada reducer recebe o estado atual e uma ação, retornando um novo estado. Eles são responsáveis por atualizar partes específicas do estado conforme as ações são despachadas.

Um exemplo básico de reducer pode ser o seguinte:

const initialState = { count: 0 };

function counterReducer(state = initialState, action) {
    switch (action.type) {
        case 'INCREMENT':
            return { ...state, count: state.count + 1 };
        case 'DECREMENT':
            return { ...state, count: state.count - 1 };
        default:
            return state;
    }
}
JavaScript

É importante lembrar que o estado e as ações devem ser imutáveis. Os reducers não devem alterar o estado existente, mas sim retornar uma nova versão dele.

Configuração dos Middlewares

Middlewares permitem que a aplicação intercepte ações que são despachadas antes que elas cheguem ao reducer. O Redux Saga é um middleware popular, que permite o tratamento de efeitos colaterais de forma eficiente usando geradores.

Para configurar os middlewares, o applyMiddleware é utilizado. Um exemplo simples de configuração com Redux Saga ficaria assim:

import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './reducers';
import rootSaga from './sagas';

const sagaMiddleware = createSagaMiddleware();
const store = createStore(
    rootReducer,
    applyMiddleware(sagaMiddleware)
);
sagaMiddleware.run(rootSaga);
JavaScript

Esta estrutura permite que sagas sejam utilizadas para gerenciar efeitos assíncronos na aplicação.

Integração com o React

Para conectar o Redux ao React, a biblioteca react-redux é essencial. O Provider é um componente que torna o store disponível para todos os componentes da aplicação.

Um exemplo de como integrar o store ao React é:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root')
);
JavaScript

Esta abordagem permite que os componentes acessem o estado do Redux e despachem ações, facilitando a gestão de estado em toda a aplicação.

Configurando o Redux Saga

Configurar o Redux Saga é fundamental para gerenciar efeitos colaterais em aplicações React. Esta seção aborda conceitos básicos sobre sagas e fornece um exemplo prático de criação.

Saga: Conceitos Básicos

Redux Saga é uma biblioteca que permite lidar com operações assíncronas e complexas. Baseada em generator functions do JavaScript, ela proporciona uma forma clara e efetiva de gerenciar fluxos de trabalho.

A principal função do Saga é interceptar ações e realizar chamadas de API ou outras tarefas assíncronas, desassociando a lógica de negócios da interface do usuário. Isso resulta em um código mais organizado e de mais fácil manutenção.

Principais conceitos:

  • Effects: São instruções que descrevem o que o Saga deve fazer. Exemplos incluem call, put e takeEvery.
  • Watchers: Monitores de ações que ativam sagas específicas em resposta a certos eventos.
  • Workers: Funções que executam a lógica quando um watcher é acionado.

Criando os Sagas de Exemplo

Para ilustrar a criação de sagas, considere um cenário onde a aplicação precisa buscar dados de um usuário. Primeiro, instale o Redux Saga com o comando:

npm install redux-saga
Terminal

Em seguida, configure o Saga middleware no arquivo de store:

import createSagaMiddleware from 'redux-saga';
const sagaMiddleware = createSagaMiddleware();
JavaScript

Agora, crie um Saga básico. Este exemplo observa uma ação chamada FETCH_USER e executa a função fetchUser:

import { takeEvery, call, put } from 'redux-saga/effects';

function* fetchUser(action) {
    const user = yield call(api.fetchUser, action.payload);
    yield put({ type: 'USER_FETCH_SUCCEEDED', user });
}

function* mySaga() {
    yield takeEvery('FETCH_USER', fetchUser);
}
JavaScript

Por fim, inicie o Saga no arquivo de configuração do Redux:

sagaMiddleware.run(mySaga);
JavaScript

Esses passos garantem que as operações assíncronas sejam tratadas de forma eficiente através do Redux Saga.

Conectando os Componentes React com o Redux

A integração dos componentes React com o Redux é essencial para gerenciar o estado da aplicação de forma eficaz. A seguir, são abordadas duas formas principais de interação: a utilização do hook useSelector para obter dados do estado e o uso do useDispatch para disparar ações.

Utilizando o hook useSelector

O hook useSelector é usado para acessar o estado global armazenado no Redux. Isso permite que os componentes React sejam reativos às mudanças nesse estado.

import { useSelector } from 'react-redux';

const MeuComponente = () => {
    const valor = useSelector((state) => state.nomeDoEstado);
    
    return <div>{valor}</div>;
};
JavaScript

Neste exemplo, nomeDoEstado é uma propriedade do estado global. Ao utilizar useSelector, o componente renderiza automaticamente quando nomeDoEstado muda. Isso promove uma gestão eficaz do estado, pois componentes consume uma parte específica do estado sem necessidade de conectar-se diretamente ao Redux.

Disparando Ações com useDispatch

O hook useDispatch permite que componentes React enviem ações para o Redux. Isso é fundamental para modificar o estado da aplicação.

import { useDispatch } from 'react-redux';
import { minhaAcao } from './acoes';

const MeuBotao = () => {
    const dispatch = useDispatch();

    const handleClick = () => {
        dispatch(minhaAcao());
    };

    return <button onClick={handleClick}>Clique aqui</button>;
};
JavaScript

Neste exemplo, quando o botão é clicado, a ação minhaAcao é disparada. Isso provoca uma atualização do estado global, acionando os reducers correspondentes. O uso de useDispatch é uma maneira prática de acionar alterações no estado, tornando a interação entre componentes e Redux fluida.

Persistência dos Estados com Redux Persist

A persistência de estados é uma característica essencial em aplicações que usam Redux. Utilizando o Redux Persist, é possível garantir que os estados sejam armazenados, permitindo uma recuperação eficiente após atualizações ou recarregamentos da página.

Configuração do Redux Persist

Para iniciar, deve-se instalar o Redux Persist com o comando:

npm install redux-persist
Terminal

Em seguida, é necessário importar o persistStore e o persistReducer nas configurações do store. O persistReducer é uma função que adapta o reducer existente para a persistência. Exemplo de configuração:

import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

const persistConfig = {
  key: 'root',
  storage,
};

const persistedReducer = persistReducer(persistConfig, rootReducer);
JavaScript

Esta configuração utiliza o local storage do navegador como armazenamento padrão.

Conexão com o Store

Com o reducer adaptado, deve-se criar a store utilizando o persistedReducer. A conexão com o Persistor também é crucial para a manutenção do estado persistente. Veja um exemplo de criação da store:

import { createStore } from 'redux';
import { persistStore } from 'redux-persist';

const store = createStore(persistedReducer);
const persistor = persistStore(store);
JavaScript

Mudanças no estado agora serão salvas automaticamente no armazenamento configurado. Para usar o persistor, ele deve ser integrado ao componente principal da aplicação.

Uso Prático no React

Para integrar o Redux Persist na aplicação React, use o PersistGate que adia a renderização da aplicação até que o estado tenha sido recuperado. O PersistGate deve envolver o componente principal:

import { PersistGate } from 'redux-persist/integration/react';

function App() {
  return (
    <PersistGate loading={null} persistor={persistor}>
      <MainComponent />
    </PersistGate>
  );
}
JavaScript

Dessa forma, quando a aplicação é carregada, ela restaura automaticamente o estado persistido. Isso proporciona uma experiência de usuário mais fluida, mantendo os dados mesmo após o fechamento do navegador.

Testando a Configuração

Para garantir que a configuração do Redux e Redux Saga está funcionando corretamente, é necessário realizar alguns testes simples.

Primeiro, é importante verificar se o store foi criado corretamente. Isso pode ser feito com o seguinte código:

console.log(store.getState());
JavaScript

Se o estado inicial do store for exibido no console, a configuração básica está correta.

Em seguida, é essencial validar se as ações estão sendo despachadas. Com um simples dispatch, pode-se confirmar que as alterações no estado ocorrem conforme esperado. Por exemplo:

store.dispatch({ type: 'ADICIONAR_ITEM', payload: novoItem });
JavaScript

Após despachar a ação, verificar o estado novamente com console.log(store.getState()); ajudará a ver se o item foi adicionado.

Além disso, para testar as sagas, pode-se observar o fluxo das ações. O middleware do Redux Saga deve logar as ações durante o processo. A configuração de um logger pode ser útil:

import createSagaMiddleware from 'redux-saga';
import { createStore, applyMiddleware } from 'redux';

const sagaMiddleware = createSagaMiddleware();
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
JavaScript

Por fim, um teste funcional pode ser realizado, garantindo que a aplicação interaja corretamente com o Redux e as sagas. Tools como o Redux DevTools podem ser muito úteis nessa etapa.

Implementação de um Caso de Uso Prático com Redux e Saga

Para implementar um caso de uso prático com Redux e Redux Saga, é necessário criar uma estrutura que gerencie o estado da aplicação de forma eficiente.

Primeiro, é essencial definir as ações. Por exemplo:

  • FETCH_DATA_REQUEST: Inicia a requisição.
  • FETCH_DATA_SUCCESS: Requisição bem-sucedida.
  • FETCH_DATA_FAILURE: Erro na requisição.

Em seguida, cria-se o reducer que irá gerenciar essas ações. O reducer pode ter o seguinte formato:

const initialState = {
  data: [],
  loading: false,
  error: null,
};

function dataReducer(state = initialState, action) {
  switch (action.type) {
    case 'FETCH_DATA_REQUEST':
      return { ...state, loading: true };
    case 'FETCH_DATA_SUCCESS':
      return { ...state, loading: false, data: action.payload };
    case 'FETCH_DATA_FAILURE':
      return { ...state, loading: false, error: action.payload };
    default:
      return state;
  }
}
JavaScript

Agora, é hora de implementar as sagas. Para isso, crie uma saga para a requisição de dados:

import { call, put, takeEvery } from 'redux-saga/effects';
import axios from 'axios';

function* fetchData() {
  try {
    const response = yield call(axios.get, 'API_URL');
    yield put({ type: 'FETCH_DATA_SUCCESS', payload: response.data });
  } catch (error) {
    yield put({ type: 'FETCH_DATA_FAILURE', payload: error.message });
  }
}

function* watchFetchData() {
  yield takeEvery('FETCH_DATA_REQUEST', fetchData);
}
JavaScript

Esses componentes básicos estabelecem um fluxo de trabalho eficiente que permite gerenciar estados assíncronos de maneira clara e estruturada.

Boas Práticas no Uso de Redux e Redux Saga

Implementar Redux e Redux Saga com eficiência é crucial para um projeto React. As boas práticas ajudam a manter o código limpo e a facilitar a manutenção.

1. Estrutura do Estado:
Definir uma estrutura clara para o estado global é fundamental. Utilize uma estrutura normalizada para facilitar as atualizações e a leitura dos dados.

2. Nomeação Consistente:
Utilizar convenções de nomenclatura consistentes para actions e reducers. Isso ajuda na identificação rápida das funcionalidades no código.

3. Dividir Sagas:
Dividir as sagas em partes menores e mais focadas. Isso torna o código mais legível e reutilizável.

4. Ações e Efeitos:
Separar as ações de efeitos colaterais. Mantê-los em sagas facilita a compreensão do fluxo de dados e permite testes mais fáceis.

5. Utilizar Middleware com Cuidado:
Adicionar apenas os middlewares necessários. O uso excessivo pode tornar o código difícil de entender e manter.

6. Testes:
Criar testes para reducers e sagas. Isso garante que a lógica funcione conforme o esperado e ajuda a evitar regressões.

7. Documentação:
Documentar as ações e o fluxo das sagas. Uma boa documentação é essencial para novos desenvolvedores e para a equipe de manutenção.

Seguir essas boas práticas ajuda a criar uma base sólida e escalável para aplicações complexas.

Links úteis

Artefato X
Artefato X
Artigos: 12