Por Que Usar TypeScript com Redux?
Redux é uma das bibliotecas mais populares para gerenciamento de estado em aplicações JavaScript. No entanto, quando os estados e ações se tornam complexos, o uso de Redux pode introduzir erros difíceis de rastrear. Ao combinar Redux com TypeScript, você pode garantir a tipagem estática de ações, estados e dispatchers, o que melhora a segurança do código e torna a refatoração mais fácil, além de fornecer feedback antecipado durante o desenvolvimento.
Configurando TypeScript com Redux
Antes de tipar as ações e estados, precisamos configurar um ambiente de desenvolvimento com React, Redux e TypeScript. Siga as etapas a seguir para configurar o projeto.
1. Instalando Dependências
Primeiro, crie um projeto React com TypeScript e instale as dependências do Redux:
npx create-react-app meu-projeto --template typescript
cd meu-projeto
npm install redux react-redux @types/react-redux
Isso instala o Redux, o React-Redux para conectar o Redux ao React, e as definições de tipos para o TypeScript.
2. Estrutura do Projeto
Vamos organizar nosso projeto com as seguintes pastas e arquivos:
src/
actions/
contadorActions.ts
reducers/
contadorReducer.ts
store/
index.ts
components/
Contador.tsx
App.tsx
Com essa estrutura, separaremos as ações, reducers e componentes para organizar o código de forma modular e escalável.
Tipando o Estado com TypeScript
O primeiro passo para tipar o Redux com TypeScript é garantir que o estado global e o estado individual dos reducers estejam corretamente definidos. Vamos começar criando um estado para um contador.
1. Definindo a Interface do Estado
Crie um arquivo chamado contadorReducer.ts
dentro da pasta reducers
com a seguinte interface para o estado:
export interface ContadorState {
valor: number;
}
const estadoInicial: ContadorState = {
valor: 0
};
A interface ContadorState
define a estrutura do estado, que neste caso contém apenas uma propriedade valor
do tipo number
.
Tipando as Ações do Redux
As ações no Redux descrevem eventos que ocorrem na aplicação e são despachadas para modificar o estado. Com TypeScript, podemos garantir que as ações sejam tipadas corretamente e que os reducers recebam sempre as ações esperadas.
1. Definindo Tipos de Ação
Vamos criar um arquivo chamado contadorActions.ts
na pasta actions
para definir os tipos e criadores de ação:
export enum ContadorActionTypes {
INCREMENTAR = 'INCREMENTAR',
DECREMENTAR = 'DECREMENTAR'
}
interface IncrementarAction {
type: ContadorActionTypes.INCREMENTAR;
}
interface DecrementarAction {
type: ContadorActionTypes.DECREMENTAR;
}
export type ContadorActions = IncrementarAction | DecrementarAction;
export const incrementar = (): IncrementarAction => ({
type: ContadorActionTypes.INCREMENTAR
});
export const decrementar = (): DecrementarAction => ({
type: ContadorActionTypes.DECREMENTAR
});
Aqui, usamos o enum
ContadorActionTypes
para definir os tipos de ação. Criamos interfaces para cada ação e, em seguida, exportamos uma união de todas as ações disponíveis como ContadorActions
. Isso permite que o TypeScript verifique se estamos passando ações válidas para o reducer.
Tipando o Reducer no Redux
O reducer é a função responsável por atualizar o estado com base nas ações despachadas. Vamos criar e tipar o reducer para o contador.
1. Criando o Reducer
Agora, no arquivo contadorReducer.ts
, adicione o código para o reducer:
import { ContadorState } from './contadorReducer';
import { ContadorActionTypes, ContadorActions } from '../actions/contadorActions';
const estadoInicial: ContadorState = {
valor: 0
};
export const contadorReducer = (state: ContadorState = estadoInicial, action: ContadorActions): ContadorState => {
switch (action.type) {
case ContadorActionTypes.INCREMENTAR:
return { ...state, valor: state.valor + 1 };
case ContadorActionTypes.DECREMENTAR:
return { ...state, valor: state.valor - 1 };
default:
return state;
}
};
O reducer é tipado com ContadorState
e ContadorActions
, garantindo que o estado e as ações sigam as estruturas definidas. O TypeScript verifica se as ações são válidas e se o estado está sendo modificado corretamente.
Configurando a Store
A store é o centro do Redux, onde o estado é mantido. Vamos configurar a store com o reducer do contador.
1. Criando a Store
Em store/index.ts
, adicione o seguinte código para configurar a store:
import { createStore } from 'redux';
import { contadorReducer } from '../reducers/contadorReducer';
export const store = createStore(contadorReducer);
Essa configuração cria a store Redux usando o reducer do contador. Agora, a store está pronta para ser conectada ao React.
Conectando Redux ao React
Agora que configuramos o Redux, vamos conectar a store ao React. Para isso, usaremos o useSelector
e useDispatch
do React-Redux, garantindo que as funções e estados sejam corretamente tipados.
1. Criando o Componente Contador.tsx
Crie um componente de contador em components/Contador.tsx
:
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { incrementar, decrementar } from '../actions/contadorActions';
import { ContadorState } from '../reducers/contadorReducer';
const Contador: React.FC = () => {
const valor = useSelector((state: ContadorState) => state.valor);
const dispatch = useDispatch();
return (
<div>
<h1>Contador: {valor}</h1>
<button onClick={() => dispatch(incrementar())}>Incrementar</button>
<button onClick={() => dispatch(decrementar())}>Decrementar</button>
</div>
);
};
export default Contador;
O useSelector
é tipado para garantir que o estado retornado seja do tipo ContadorState
. O useDispatch
é usado para despachar as ações incrementar
e decrementar
.
Conectando o Componente ao App
No arquivo App.tsx
, conecte o componente Contador
à store Redux:
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './store';
import Contador from './components/Contador';
const App: React.FC = () => {
return (
<Provider store={store}>
<Contador />
</Provider>
);
};
export default App;
O Provider
é usado para disponibilizar a store Redux ao componente Contador
. Agora, sua aplicação está conectada ao Redux com TypeScript garantindo a segurança de tipos.
Boas Práticas ao Usar Redux com TypeScript
- Use
enum
para Ações: Defina suas ações comoenum
para garantir que os tipos de ação sejam consistentes e fáceis de manter. - Tipagem Rigorosa para Estados e Ações: Sempre defina interfaces claras para o estado e para as ações. Isso ajuda a prevenir erros e torna o código mais legível.
- Use
useSelector
euseDispatch
com Tipagem: Garanta que as funçõesuseSelector
euseDispatch
estejam corretamente tipadas, melhorando a segurança e previsibilidade do código. - Separe Ações e Reducers: Organize as ações e reducers em arquivos separados para melhorar a manutenibilidade e escalabilidade do projeto.
Conclusão
Combinando Redux com TypeScript, você pode criar aplicações com gerenciamento de estado fortemente tipado e previsível. Tipar ações, estados e reducers garante que o fluxo de dados seja seguro e consistente em toda a aplicação, tornando o processo de desenvolvimento mais eficiente e menos propenso a erros. Seguindo boas práticas, como a tipagem rigorosa e a organização modular do código, seu projeto se tornará mais escalável e fácil de manter.