O gerenciamento de estado em aplicações React complexas apresenta desafios únicos. Redux oferece uma arquitetura centralizada e previsível, enquanto a Context API fornece uma solução nativa mais simples. A combinação dessas tecnologias pode maximizar os benefícios de ambas as abordagens.
Redux facilita o controle de mudanças através de actions e reducers, mas pode gerar código verboso. A Context API elimina prop drilling sem configuração adicional, porém não oferece a mesma estrutura para aplicações grandes. Integrar ambas permite segmentar responsabilidades conforme a complexidade de cada funcionalidade.
Configuração Inicial do Projeto
Inicie criando um novo projeto React com as ferramentas necessárias. Certifique-se de ter Node.js versão 14 ou superior instalado no sistema.
npx create-react-app redux-context-integration
cd redux-context-integration
npm install redux react-redux @reduxjs/toolkit
Utilize o Redux Toolkit para simplificar a configuração e reduzir código boilerplate. Esta ferramenta oficial oferece utilities que tornam o Redux mais produtivo e eficiente.
Configuração do Redux Store
Crie a estrutura do Redux com Redux Toolkit, que simplifica significantly a configuração tradicional:
// src/store/counterSlice.js
import { createSlice } from \'@reduxjs/toolkit\'
const counterSlice = createSlice({
name: \'counter\',
initialState: {
value: 0,
history: []
},
reducers: {
increment: (state) => {
state.value += 1
state.history.push(Incrementado para ${state.value})
},
decrement: (state) => {
state.value -= 1
state.history.push(Decrementado para ${state.value})
},
incrementByAmount: (state, action) => {
state.value += action.payload
state.history.push(Incrementado por ${action.payload})
}
}
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
Configure o store principal combinando todos os reducers da aplicação:
// src/store/index.js
import { configureStore } from \'@reduxjs/toolkit\'
import counterReducer from \'./counterSlice\'
const store = configureStore({
reducer: {
counter: counterReducer
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [\'persist/PERSIST\']
}
})
})
export default store
Implementação da Context API
A Context API gerenciará dados de sessão, preferências do usuário e estados temporários que não precisam persistir no Redux:
// src/contexts/AppContext.js
import React, { createContext, useContext, useReducer } from \'react\'
const AppContext = createContext()
const initialState = {
theme: \'light\',
language: \'pt-BR\',
notifications: [],
tempData: {}
}
function appReducer(state, action) {
switch (action.type) {
case \'SET_THEME\':
return { ...state, theme: action.payload }
case \'ADD_NOTIFICATION\':
return {
...state,
notifications: [...state.notifications, action.payload]
}
case \'REMOVE_NOTIFICATION\':
return {
...state,
notifications: state.notifications.filter(
(notification) => notification.id !== action.payload
)
}
case \'SET_TEMP_DATA\':
return {
...state,
tempData: { ...state.tempData, ...action.payload }
}
default:
return state
}
}
export function AppProvider({ children }) {
const [state, dispatch] = useReducer(appReducer, initialState)
return (
{children}
)
}
export const useApp = () => {
const context = useContext(AppContext)
if (!context) {
throw new Error(\'useApp deve ser usado dentro de AppProvider\')
}
return context
}
Integração Completa no App Principal
Configure o App.js para utilizar ambos os providers simultaneamente:
// src/App.js
import React from \'react\'
import { Provider } from \'react-redux\'
import store from \'./store\'
import { AppProvider } from \'./contexts/AppContext\'
import Counter from \'./components/Counter\'
import ThemeToggle from \'./components/ThemeToggle\'
function App() {
return (
)
}
export default App
Componente Prático: Counter Integrado
Desenvolva um componente que demonstra a integração prática entre Redux e Context API:
// src/components/Counter.js
import React from \'react\'
import { useSelector, useDispatch } from \'react-redux\'
import { increment, decrement, incrementByAmount } from \'../store/counterSlice\'
import { useApp } from \'../contexts/AppContext\'
function Counter() {
const count = useSelector((state) => state.counter.value)
const history = useSelector((state) => state.counter.history)
const dispatch = useDispatch()
const { state: appState, dispatch: appDispatch } = useApp()
const handleIncrement = () => {
dispatch(increment())
appDispatch({
type: \'ADD_NOTIFICATION\',
payload: {
id: Date.now(),
message: \'Contador incrementado!\',
type: \'success\'
}
})
}
const handleCustomIncrement = () => {
const amount = parseInt(appState.tempData.customAmount) || 1
dispatch(incrementByAmount(amount))
}
return (
counter-container theme-${appState.theme}}>
Contador: {count}
appDispatch({
type: \'SET_TEMP_DATA\',
payload: { customAmount: e.target.value }
})}
/>
Histórico:
{history.slice(-5).map((entry, index) => (
- {entry}
))}
{appState.notifications.map((notification) => (
notification ${notification.type}}>
{notification.message}
))}
)
}
export default Counter
Estratégias de Otimização
Para evitar re-renderizações desnecessárias, utilize React.memo e useMemo estrategicamente. O desenvolvimento web moderno exige atenção especial à performance quando combinamos múltiplas fontes de estado.
// src/hooks/useOptimizedSelector.js
import { useSelector } from \'react-redux\'
import { useMemo } from \'react\'
export const useOptimizedSelector = (selector, dependencies = []) => {
return useSelector(
useMemo(() => selector, dependencies)
)
}
// Exemplo de uso
const count = useOptimizedSelector(
(state) => state.counter.value,
[]
)
Padrões de Separação de Responsabilidades
Estabeleça regras claras para decidir quando usar Redux versus Context API:
- Redux: Estado global da aplicação, dados persistentes, histórico de ações
- Context API: Preferências de UI, estados temporários, dados de sessão
- Estado local: Estados específicos de componentes, formulários simples
Middleware Personalizado para Integração
Desenvolva middleware customizado para sincronizar ações entre Redux e Context:
// src/middleware/contextSync.js
const contextSyncMiddleware = (contextDispatch) => (store) => (next) => (action) => {
const result = next(action)
// Sincronizar ações específicas com o contexto
if (action.type.startsWith(\'counter/\')) {
contextDispatch({
type: \'ADD_NOTIFICATION\',
payload: {
id: Date.now(),
message: Ação Redux: ${action.type},
type: \'info\'
}
})
}
return result
}
export default contextSyncMiddleware
Testes da Integração
Implemente testes que validem o funcionamento conjunto das duas abordagens:
// src/__tests__/integration.test.js
import React from \'react\'
import { render, screen, fireEvent } from \'@testing-library/react\'
import { Provider } from \'react-redux\'
import store from \'../store\'
import { AppProvider } from \'../contexts/AppContext\'
import Counter from \'../components/Counter\'
const renderWithProviders = (component) => {
return render(
{component}
)
}
test(\'integração Redux e Context API\', () => {
renderWithProviders( )
const incrementButton = screen.getByText(\'+1\')
fireEvent.click(incrementButton)
expect(screen.getByText(\'Contador: 1\')).toBeInTheDocument()
expect(screen.getByText(\'Contador incrementado!\')).toBeInTheDocument()
})
Vantagens e Considerações
A integração oferece flexibilidade máxima no gerenciamento de estado. Redux mantém dados críticos de forma previsível, enquanto Context API gerencia estados ephemeros sem overhead. Esta abordagem permite otimizações específicas e separação clara de responsabilidades.
Monitore a complexidade cuidadosamente. Estabeleça convenções claras sobre qual ferramenta usar em cada cenário. Documente padrões de uso para manter consistência entre desenvolvedores da equipe.
Consider ferramentas como Web.dev para monitorar performance e otimizar renderizações. A combinação Redux + Context API, quando bem implementada, resulta em aplicações mais maintíveis e escaláveis.
Comentários
0Inicie sessão para deixar um comentário
Iniciar sessãoSé el primero en comentar