JavaScript Avançado

O que Todo Dev Deve Saber sobre Hooks Avançados em React: useReducer, useContext e useImperativeHandle

7 min de leitura

O que Todo Dev Deve Saber sobre Hooks Avançados em React: useReducer, useContext e useImperativeHandle

useReducer: Gerenciamento de Estado Complexo O é o hook ideal quando seu estado tem múltiplas sub-valores ou a lógica de atualização é complexa. Diferente do , ele utiliza um padrão reducer similar ao Redux, onde ações disparadas modificam o estado de forma previsível. A estrutura consiste em três elementos: o estado atual, uma função reducer que processa ações, e a função dispatch para dispará-las. Considere um carrinho de compras onde você precisa adicionar produtos, remover e limpar o carrinho simultaneamente: Essa abordagem torna o código mais previsível e facilita testes, especialmente em aplicações com lógica complexa. useContext: Compartilhamento de Estado Global O elimina a necessidade de prop drilling ao criar um canal direto de comunicação entre componentes distantes na árvore. Combinado com , cria um sistema robusto de gerenciamento de estado. A implementação envolve criar um Context, um Provider que envolve a aplicação, e consumidores que acessam os valores. Veja um exemplo prático com tema da aplicação: Este padrão é

<h2>useReducer: Gerenciamento de Estado Complexo</h2>

<p>O <code>useReducer</code> é o hook ideal quando seu estado tem múltiplas sub-valores ou a lógica de atualização é complexa. Diferente do <code>useState</code>, ele utiliza um padrão reducer similar ao Redux, onde ações disparadas modificam o estado de forma previsível.</p>

<p>A estrutura consiste em três elementos: o estado atual, uma função reducer que processa ações, e a função dispatch para dispará-las. Considere um carrinho de compras onde você precisa adicionar produtos, remover e limpar o carrinho simultaneamente:</p>

<pre><code class="language-javascript">import React, { useReducer } from &#039;react&#039;;

const initialState = { items: [], total: 0 };

function cartReducer(state, action) {

switch(action.type) {

case &#039;ADD_ITEM&#039;:

return {

items: [...state.items, action.payload],

total: state.total + action.payload.price

};

case &#039;REMOVE_ITEM&#039;:

return {

items: state.items.filter((_, i) =&gt; i !== action.payload),

total: state.total - state.items[action.payload].price

};

case &#039;CLEAR&#039;:

return initialState;

default:

return state;

}

}

function ShoppingCart() {

const [cart, dispatch] = useReducer(cartReducer, initialState);

return (

&lt;div&gt;

&lt;button onClick={() =&gt; dispatch({

type: &#039;ADD_ITEM&#039;,

payload: { name: &#039;Produto&#039;, price: 50 }

})}&gt;

Adicionar Item

&lt;/button&gt;

&lt;p&gt;Total: R$ {cart.total}&lt;/p&gt;

&lt;/div&gt;

);

}</code></pre>

<p>Essa abordagem torna o código mais previsível e facilita testes, especialmente em aplicações com lógica complexa.</p>

<h2>useContext: Compartilhamento de Estado Global</h2>

<p>O <code>useContext</code> elimina a necessidade de prop drilling ao criar um canal direto de comunicação entre componentes distantes na árvore. Combinado com <code>useReducer</code>, cria um sistema robusto de gerenciamento de estado.</p>

<p>A implementação envolve criar um Context, um Provider que envolve a aplicação, e consumidores que acessam os valores. Veja um exemplo prático com tema da aplicação:</p>

<pre><code class="language-javascript">import React, { createContext, useContext, useReducer } from &#039;react&#039;;

const ThemeContext = createContext();

function themeReducer(state, action) {

switch(action.type) {

case &#039;TOGGLE_THEME&#039;:

return { ...state, isDark: !state.isDark };

case &#039;SET_COLOR&#039;:

return { ...state, primaryColor: action.payload };

default:

return state;

}

}

function ThemeProvider({ children }) {

const [theme, dispatch] = useReducer(themeReducer, {

isDark: false,

primaryColor: &#039;#3498db&#039;

});

return (

&lt;ThemeContext.Provider value={{ theme, dispatch }}&gt;

{children}

&lt;/ThemeContext.Provider&gt;

);

}

function useTheme() {

const context = useContext(ThemeContext);

if (!context) {

throw new Error(&#039;useTheme deve ser usado dentro de ThemeProvider&#039;);

}

return context;

}

function Header() {

const { theme, dispatch } = useTheme();

return (

&lt;header style={{

background: theme.isDark ? &#039;#000&#039; : &#039;#fff&#039;,

color: theme.primaryColor

}}&gt;

&lt;button onClick={() =&gt; dispatch({ type: &#039;TOGGLE_THEME&#039; })}&gt;

{theme.isDark ? &#039;☀️&#039; : &#039;🌙&#039;}

&lt;/button&gt;

&lt;/header&gt;

);

}</code></pre>

<p>Este padrão é especialmente útil para contextos que precisam ser acessados em múltiplos níveis sem passar props explicitamente.</p>

<h2>useImperativeHandle: Expondo Métodos de Componentes</h2>

<p>O <code>useImperativeHandle</code> permite que componentes filhos exponham métodos imperativos que podem ser chamados pelo pai através de refs. Use com moderação, pois quebra o padrão declarativo do React, mas é essencial para integrações com bibliotecas externas.</p>

<p>Considere um formulário onde o pai precisa resetar os dados programaticamente:</p>

<pre><code class="language-javascript">import React, { useRef, useImperativeHandle, forwardRef, useState } from &#039;react&#039;;

const Form = forwardRef(function Form(props, ref) {

const [formData, setFormData] = useState({ name: &#039;&#039;, email: &#039;&#039; });

useImperativeHandle(ref, () =&gt; ({

reset: () =&gt; setFormData({ name: &#039;&#039;, email: &#039;&#039; }),

getData: () =&gt; formData,

setData: (data) =&gt; setFormData(data)

}), [formData]);

const handleChange = (e) =&gt; {

const { name, value } = e.target;

setFormData(prev =&gt; ({ ...prev, [name]: value }));

};

return (

&lt;form&gt;

&lt;input

name=&quot;name&quot;

value={formData.name}

onChange={handleChange}

placeholder=&quot;Nome&quot;

/&gt;

&lt;input

name=&quot;email&quot;

value={formData.email}

onChange={handleChange}

placeholder=&quot;Email&quot;

/&gt;

&lt;/form&gt;

);

});

function App() {

const formRef = useRef();

return (

&lt;div&gt;

&lt;Form ref={formRef} /&gt;

&lt;button onClick={() =&gt; formRef.current.reset()}&gt;

Limpar Formulário

&lt;/button&gt;

&lt;/div&gt;

);

}</code></pre>

<p>Utilize este hook quando precisar controlar imperativamentebehaviors específicos como validação, envio de dados ou interação com APIs externas.</p>

<h2>Combinando os Três Hooks em Prática</h2>

<p>Integrar os três hooks em uma única solução oferece máxima flexibilidade. Um sistema de notificações globais exemplifica perfeitamente:</p>

<pre><code class="language-javascript">const NotificationContext = createContext();

function notificationReducer(state, action) {

switch(action.type) {

case &#039;ADD&#039;:

return [...state, { id: Date.now(), ...action.payload }];

case &#039;REMOVE&#039;:

return state.filter(n =&gt; n.id !== action.payload);

default:

return state;

}

}

function NotificationProvider({ children }) {

const [notifications, dispatch] = useReducer(notificationReducer, []);

const notificationRef = useRef();

useImperativeHandle(notificationRef, () =&gt; ({

add: (message, type = &#039;info&#039;) =&gt;

dispatch({ type: &#039;ADD&#039;, payload: { message, type } })

}), []);

return (

&lt;NotificationContext.Provider value={{ notifications, dispatch, notificationRef }}&gt;

{children}

&lt;/NotificationContext.Provider&gt;

);

}</code></pre>

<p>Esta combinação criar sistemas escaláveis que mantêm código limpo e testável.</p>

<h2>Conclusão</h2>

<p>Dominando <code>useReducer</code>, <code>useContext</code> e <code>useImperativeHandle</code>, você terá ferramentas suficientes para gerenciar estado complexo, compartilhá-lo globalmente e integrar comportamentos imperativos quando necessário. O segredo é escolher a ferramenta certa para cada situação: use <code>useReducer</code> para lógica complexa, <code>useContext</code> para compartilhamento entre componentes distantes, e <code>useImperativeHandle</code> apenas quando o padrão declarativo não for suficiente. Pratique combinando-os em pequenos projetos para internalizar esses conceitos.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://react.dev/reference/react/useReducer" target="_blank" rel="noopener noreferrer">React Hooks Documentation - useReducer</a></li>

<li><a href="https://react.dev/reference/react/useContext" target="_blank" rel="noopener noreferrer">React Hooks Documentation - useContext</a></li>

<li><a href="https://react.dev/reference/react/useImperativeHandle" target="_blank" rel="noopener noreferrer">React Hooks Documentation - useImperativeHandle</a></li>

<li><a href="https://epicreact.dev/" target="_blank" rel="noopener noreferrer">Advanced React Patterns - Kent C. Dodds</a></li>

<li><a href="https://react.dev/reference/react/hooks" target="_blank" rel="noopener noreferrer">React Official Blog - Hooks Introduction</a></li>

</ul>

Comentários

Mais em JavaScript Avançado

Arquitetura Interna do Node.js: libuv, Thread Pool e Event Loop na Prática
Arquitetura Interna do Node.js: libuv, Thread Pool e Event Loop na Prática

O Coração do Node.js: Entendendo a Arquitetura Node.js é famoso por sua capac...

Dominando Performance em Node.js: Profiling com --inspect e Clinic.js em Projetos Reais
Dominando Performance em Node.js: Profiling com --inspect e Clinic.js em Projetos Reais

Entendendo a Importância do Profiling em Node.js Performance é mais que uma m...

AbortController e Cancelamento de Operações Assíncronas: Do Básico ao Avançado
AbortController e Cancelamento de Operações Assíncronas: Do Básico ao Avançado

AbortController: Dominando o Cancelamento de Operações Assíncronas O é uma AP...