Gerenciamento de estado global com useReducer
e Context API
O uso do useReducer em conjunto com a Context API do React é preferível ao useState para gerenciar o estado global, especialmente quando há o risco de perder o estado do contexto. A razão principal reside na forma como o useReducer
manipula as atualizações do estado, o que evita o problema de o desenvolvedor não utilizar o spread operator para copiar os valores do estado anterior e, assim, não perder o estado global do contexto.
1. Definindo ações
Começamos definindo as ações que serão despachadas pelo redutor. Criaremos um arquivo chamado actions.js
:
// actions.js
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const RESET = 'RESET';
export { INCREMENT, DECREMENT, RESET };
2. Criando a função redutora
Agora, implementaremos a função redutora no arquivo reducer.js
. Ele atualizará o estado de acordo com o tipo da ação:
// reducer.js
import { INCREMENT, DECREMENT, RESET } from './actions';
const reducer = (state, action) => {
switch (action.type) {
case INCREMENT:
return { count: state.count + 1 };
case DECREMENT:
return { count: state.count - 1 };
case RESET:
return { count: 0 };
default:
return state;
}
};
export default reducer;
3. Criando o contexto
Vamos criar o contexto que armazenará o estado global e fornecerá funções específicas do dispatch
para cada tipo de ação. Isso será feito no arquivo Context.js
:
// Context.js
import React, { createContext, useReducer } from 'react';
import reducer from './reducer';
const initialState = { count: 0 };
const AppContext = createContext();
const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const increment = () => {
dispatch({ type: INCREMENT });
};
const decrement = () => {
dispatch({ type: DECREMENT });
};
const reset = () => {
dispatch({ type: RESET });
};
return (
<AppContext.Provider value={{ state, increment, decrement, reset }}>
{children}
</AppContext.Provider>
);
};
export { AppContext, AppProvider };
4. Utilizando o contexto em componentes
Agora, podemos usar esse contexto em nossos componentes. Abaixo, um exemplo simples com um componente de contagem:
// Counter.js
import React, { useContext } from 'react';
import { AppContext } from './Context';
const Counter = () => {
const { state, increment, decrement, reset } = useContext(AppContext);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
};
export default Counter;
5. Integrando com o componente principal
Integramos nosso contexto global no componente principal (App.js
), onde o componente Counter
é renderizado:
// App.js
import React from 'react';
import { AppProvider } from './Context';
import Counter from './Counter';
const App = () => {
return (
<AppProvider>
<div>
<h1>Counter App</h1>
<Counter />
</div>
</AppProvider>
);
};
export default App;
A principal vantagem do uso de useReducer
é evidenciada quando lidamos com estados mais complexos, pois ele garante a imutabilidade do estado. Enquanto o useState
pode levar a problemas se o desenvolvedor esquecer de copiar o estado anterior ao realizar uma atualização, já o useReducer
lida naturalmente com essa situação, uma vez que é necessário fornecer o novo estado com base no estado anterior. Isso minimiza os riscos de perder o estado global do contexto e contribui para um gerenciamento de estado mais robusto e previsível.