Entendendo o useReducer do React
Tags: javascript, react, braziliandevs
Published: 2024-05-26 16:11:14

---
title: Entendendo o useReducer do React
published: true
description: Explicando como funciona e casos de uso para o useReducer.
tags: javascript, react, braziliandevs
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cdsexlm94aiijaotb9u3.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2024-05-26 00:50 +0000
---

Neste artigo vamos entender como o `useReducer` funciona e dar um exemplo da vida real de uso para ele, mas primeiro vamos entender o que é esse hook. Uma definição curta para o `useReducer` é que ele permite você utilizar um `reducer` em seu componente. Ele entra como uma boa opção quando surge a necessidade de lidar com dados ou lógicas mais complexas para manipular estado. Esse hook utiliza o padrão de `reducers` que foi popularizado com o `redux`, uma biblioteca de gerenciamento de estado muito famosa e que ainda é amplamente utilizada no mercado.

## Pegando um contexto

Antes iniciarmos com o `useReducer` primeiro vamos entender o que seria um `reducer`. Um `reducer` é uma função que recebe o estado atual, uma ação e retorna um novo estado. Simples assim. Por convenção, geralmente os redutores, tem instruções `switch` para dizer como o estado será atualizado e retornar esse novo valor.

Para entender melhor essa ideia, podemos imaginar uma receita: você tem os ingredientes(o estado atual) e uma instrução(ação), o resultado é um prato preparado por você(novo estado).

Agora vamos ver como um `reducer` se parece:
```js
const initialState = { count: 0 };

function reducer(state = initialState, action) {
  switch(action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
}
```

No exemplo o `reducer` faz exatamente o que explicamos anteriormente, ele é uma função que recebe o estado atual(`state`) e uma ação(`action`) e retorna um novo estado dependendo de qual ação será executada. Nesse exemplo existem duas possibilidades de ação, aumentar o contador(`INCREMENT`) ou diminuir(`DECREMENT`). Então se o tipo de ação for `INCREMENT` o valor retornado será `{ count: 1 }`, agora se for uma ação do tipo `DECREMENT` o valor retornado será `{ count: -1 }` e assim sucessivamente.

Um ponto muito importante dos `reducers` é a sua imutabilidade, eles sempre devem retornar um novo estado ao invés de modificar o estado atual. Isso garante que o estado seja imutável e pode ajudar a evitar bugs.

## Como o "useReducer" funciona

Para entender o hook `useReducer` precisamos ter atenção as três principais partes do hook: a função redutora, a inicialização do estado e o despachante de ações. Vejamos um a um:
- Função redutora: é a função que diz como o estado vai ser atualizado quando receber uma ação. Essa função recebe dois parâmetros, o estado atual e a ação que deve ser executada, a partir disso um novo estado é retornado.
- Inicialização do estado: será o seu estado, é um objeto consumido pela sua função redutora.
- Despachante de ações: é a função que recebe um objeto por parâmetro, esse objeto é a ação que contém as instruções necessárias para a função redutora atualizar seu estado.

Agora vamos para a sintaxe:
```js
const [state, dispatch] = useReducer(reducer, initialState);
```

Aqui temos o seguinte:

- `state`: estado atual.
- `dispatch`: despachante de ações.
- `reducer`: função redutora.
- `initialState`: estado inicial, um objeto literal.

## Exemplo da vida real

Para entender qual é a aplicabilidade do `useReducer` partiremos para um exemplo bem comum. Imagine que você precisa criar um carrinho de compras que pode ter as seguintes funcionalidades, adicionar, remover e limpar os itens.

Começaremos definindo nosso `reducer` em um arquivo chamado `reducer.js` :
```js
// reducer.js
export const initialState = {
  items: [],
  totalAmount: 0
}

export function cartReducer(state, action) {
  switch (action.type) {
    case 'ADD_ITEM':
      const updatedItems = [...state.items, action.item];
      const updatedTotalAmount = state.totalAmount + action.item.price;
      return {
        ...state,
        items: updatedItems,
        totalAmount: updatedTotalAmount,
      };
    case 'REMOVE_ITEM':
      const filteredItems = state.items.filter(item => item.id !== action.id);
      const itemToRemove = state.items.find(item => item.id === action.id);
      const decreasedTotalAmount = state.totalAmount - itemToRemove.price;
      return {
        ...state,
        items: filteredItems,
        totalAmount: decreasedTotalAmount,
      };
    case 'CLEAR_CART':
      return initialState;
    default:
      return state;
  }
}
```

Agora precisamos criar nosso componente e utilizar o `useReducer` dentro dele:
```jsx
// ShoppingCart.js
import { useReducer } from "react";

import { cartReducer, initialState } from "./shopping-cart.reducer";

export const ShoppingCart = () => {
  const [cart, dispatch] = useReducer(cartReducer, initialState);

  const hasCartItems = cart.items.length > 0;

  const addItemHandler = (item) => {
    dispatch({ type: "ADD_ITEM", item });
  };

  const removeItemHandler = (id) => {
    dispatch({ type: "REMOVE_ITEM", id });
  };

  const clearCartHandler = () => {
    dispatch({ type: "CLEAR_CART" });
  };

  return (
    <div>
      <h2>Meu carrinho</h2>
      {hasCartItems && (
        <ul>
          {cart.items.map((item) => (
            <li key={item.id}>
              {item.name} - ${item.price}
              <button onClick={() => removeItemHandler(item.id)}>
                Remover
              </button>
            </li>
          ))}
        </ul>
      )}
      <div>Total: ${cart.totalAmount}</div>
      <button onClick={clearCartHandler}>Limpar Carrinho</button>
      <button
        onClick={() =>
          addItemHandler({ id: "shoes-1", name: "Tênis", price: 99 })
        }
      >
        Adicionar "Tênis" ao carrinho
      </button>
      <button
        onClick={() =>
          addItemHandler({ id: "shirt-1", name: "Camiseta", price: 39 })
        }
      >
        Adicionar "Camiseta" ao carrinho
      </button>
    </div>
  );
};
```

No arquivo `reducer.js`, definimos o estado inicial do carrinho e quais ações podem ser aplicadas para atualizá-lo. As ações disponíveis são `ADD_ITEM`, `REMOVE_ITEM` e `CLEAR_CART`. Cada vez que uma dessas ações é executada, uma cópia do estado atual é criada, atualizada conforme a ação, e um novo estado atualizado é retornado.

Já no componente `ShoppingCart`, utilizamos o `useReducer` para gerenciar o estado do carrinho. No componente, temos três funções, cada uma responsável por uma ação específica. Essas funções despacham ações para o nosso `reducer`, que se encarrega de atualizar o estado e retorná-lo. O componente `ShoppingCart` renderiza os itens do carrinho, o valor total e os botões de ação para adicionar, remover e limpar o carrinho.

## Conclusão

Neste artigo aprendemos como o hook `useReducer` funciona e também um pouco sobre o conceito por trás dele. Também entendemos como ele pode ser uma ferramenta poderosa, que facilita o gerenciamento de estados mais complexos em seus componentes, principalmente quando a lógica de atualização do estado precisa de várias ações.

Código do exemplo neste [sandbox](https://codesandbox.io/p/sandbox/epic-sunset-lq95nw).

Espero que este artigo tenha ajudado você de alguma forma a entender melhor o `useReducer` e como ele pode ser aplicado em casos reais para gerenciar estados complexos. Se você gostou dessa leitura e ficou interessado em se aprofundar ainda mais nesse assunto, recomendo as leituras a seguir, apenas em inglês:

- [How to Use Flux to Manage State in ReactJS](https://www.freecodecamp.org/news/how-to-use-flux-in-react-example/)
- [Scaling up with reducer and context](https://react.dev/learn/scaling-up-with-reducer-and-context)

Por hoje é isso e obrigado por ler até aqui!