Desafios Avançados em Redux: Teste seus Conhecimentos
Exploro padrões modernos de estado, fluxos assíncronos, organização de domínios e estratégias de teste com Redux Toolkit.
Inspirado no desafio: desafi os-avancados-em-redux-para-testar-seus-conhecimentos.md.
1) Estruturando o estado para aplicações grandes
Em apps com muitos recursos, convém dividir o estado por domínio e usar slices por feature.
A normalização facilita operações de busca, inserção e remoção, mantendo o estado estável e previsível.
- Utilize createEntityAdapter para gerenciar coleções de entidades (IDs, entidades mapeadas).
- Separe domínios em slices distintos para reduzir acoplamento e facilitar evolução futura.
- Adoção de imutabilidade via Immer (RTK facilita atualizações imutáveis sem boilerplate).
- Prefira operações atômicas em reducers e delegue side effects para thunks ou RTK Query.
Dicas: mantenha a estrutura de selectors enxuta e crie selectors específicos por domínio para evitar re-renders desnecessários.
2) Fluxos assíncronos avançados com Redux Toolkit
Requisições assíncronas exigem estratégias para evitar condições de corrida, latência percebida e inconsistências de estado.
O RTK oferece padrões consistentes para fetch, cancelamento e atualização otimista.
- Uso de createAsyncThunk com estado de loading para cada operação.
- Prevenção de requisições concorrentes com a função condition (evita iniciar uma nova chamada se já houver pendente).
- Cancelamento de requisições ao despachar uma nova operação relevante (quando aplicável).
- Atualizações otimistas em operações de escrita com rollback em caso de erro.
Exemplos práticos incluem fetch de listas com refetch on focus, cancelamento de chamadas antigas e sincronização entre várias fatias.
// Exemplo avançado: createEntityAdapter + createAsyncThunk + selectors
import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit';
const postsAdapter = createEntityAdapter();
const initialState = postsAdapter.getInitialState({
loading: 'idle',
error: null,
});
export const fetchPosts = createAsyncThunk(
'posts/fetch',
async () => {
const res = await fetch('/api/posts');
if (!res.ok) throw new Error('Network response was not ok');
return res.json();
}
);
export const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {
addPost: postsAdapter.addOne,
updatePost: postsAdapter.updateOne,
},
extraReducers: (builder) => {
builder
.addCase(fetchPosts.pending, (state) => { state.loading = 'loading'; })
.addCase(fetchPosts.fulfilled, (state, action) => {
postsAdapter.setAll(state, action.payload);
state.loading = 'idle';
})
.addCase(fetchPosts.rejected, (state, action) => {
state.loading = 'idle';
state.error = action.error?.message;
});
}
});
export const { selectAll: selectAllPosts } = postsAdapter.getSelectors(
(state) => state.posts
);
export const postsSelectors = postsAdapter.getSelectors(state => state.posts);
export default postsSlice.reducer;
3) Seletores complexos e memoização
A memoização correta evita re-renders desnecessários, especialmente em listas grandes ou quando o estado muda apenas parcialmente.
Combine selectors simples com createSelector para compor lógica mais avançada.
- Crie selectors específicos por domínio para reduzir o escopo de dependências.
- Combine inputs com inputs selectors que não mudam com frequência.
- Evite dependências de props não estáveis sem necessidade – isso pode invalidar memorization.
- Teste por edge cases: atualizações mínimas, adições/removerem, e estados de carregamento simultâneos.
Dicas rápidas: prefira obter dados já em formato pronto (entidades, IDs) para facilitar a composição de selectors.
4) Testes e robustez de Redux
Garantir comportamento previsível em Redux exige uma estratégia de testes que cubra slices, thunks e integrações com a store.
Foque em testes unitários de reducers/slices e testes de integração para fluxos assíncronos.
- Testes de reducer/slice: verifique atualizações imutáveis, efeitos de reducers e estados intermediários.
- Testes de thunks: simule chamadas assíncronas com mocks de API, valide estados de loading, success e error.
- Testes de selectors: assegure que a memoização retorna valores estáveis para entradas equivalentes.
- Integração com RTK Query (quando utilizado): valide cache, invalidations e refetches.
Dicas de prática incluem a criação de utilitários de teste para o store e mocks consistentes de dados de domínio.
Receba mais conteúdo técnico
Gostou deste guia? Continue explorando: leia outros posts para aprofundar seu domínio sobre Redux e padrões de frontend.
Sou Apaixonado pela programação e estou trilhando o caminho de ter cada diz mais conhecimento e trazer toda minha experiência vinda do Design para a programação resultando em layouts incríveis e idéias inovadoras! Conecte-se Comigo!