DEV Community

Cover image for React Hooks vs. Redux: Hooks e Context substituem Redux?
Ivan Trindade
Ivan Trindade

Posted on

React Hooks vs. Redux: Hooks e Context substituem Redux?

O Redux Toolkit reduz a quantidade de complexidade de introduzir o Redux em nossa aplicação. No entanto, usando a API React Context além dos Hooks useContext e useReducer, que foram introduzidos no React v16.8, podemos implementar um gerenciamento de estado robusto e evitar a perfuração de prop sem a necessidade de importar bibliotecas adicionais.

Na melhor das hipóteses, isso o torna uma solução imperfeita para gerenciamento de estado em aplicações React. E, no entanto, muitos desenvolvedores React adotam como padrão o Redux para gerenciamento de estado sem considerar outras alternativas.

Neste tutorial, apresentaremos a API React Context para gerenciamento de estado e explicaremos como os React Hooks e a Context API podem substituir o Redux.

Abordaremos o seguinte:

  • O que é gerenciamento de estado no React?
  • Redux x React Hooks e a Context API
  • O que é Redux?
  • O que é a Context API?
  • O que são os hooks useContext e useReducer?
  • Como usar o hook useReducer com React Context

O que é gerenciamento de estado no React?

Para lidar com dados entre componentes desconectados no React, os desenvolvedores usam o prop drill.

Não há nenhum estado global que os componentes possam acessar. Portanto, se você quiser passar dados de um componente de nível superior para, por exemplo, um componente de quinto nível, você terá que passar os dados como prop em cada nível da árvore até chegar ao componente desejado.

Isso resulta em escrever uma tonelada de código extra e fornecer aos componentes propriedades que eles nunca usarão também afeta seu design arquitetônico.

Para resolver esse problema, precisamos fornecer um estado global que todos os componentes possam acessar, não importa o quão profundamente aninhados eles estejam.

Ao resolver esse problema, o Redux, uma biblioteca JavaScript de código aberto para gerenciar o estado da aplicação, tornou-se a solução ideal para desenvolvedores React.

Redux x React Hooks e a Context API

Até certo ponto, o Redux funciona bem para gerenciamento de estado em aplicações React e tem algumas vantagens. No entanto, sua verbosidade torna realmente difícil de aprender, e o código extra necessário para fazê-lo funcionar pode introduzir muita complexidade desnecessária.

Por outro lado, com React Hooks e a Context API, não há necessidade de instalar bibliotecas externas ou adicionar um monte de arquivos e pastas para fazer nossa aplicação funcionar. Isso torna uma abordagem muito mais simples e direta para lidar com o gerenciamento de estado global em aplicações React.

Vamos dar uma olhada mais de perto no Redux, React Hooks e na Context API para ver como elas funcionam, quais desafios os desenvolvedores enfrentam ao usar essas ferramentas e como o uso de React Hooks e Context pode ajudá-lo a superar alguns problemas comuns associados ao Redux.

O que é Redux?

De acordo com a documentação do Redux , o Redux é um contêiner de estado previsível para aplicações JavaScript que nos ajuda a escrever aplicações que se comportam de maneira consistente, executadas em ambientes diferentes e fáceis de testar.

Uma desvantagem do prop drill é que ele exige que escrevamos uma quantidade considerável de código extra para acessar os dados de um componente de nível superior. Com o Redux, essa desvantagem se torna mais grave, pois requer código adicional para configurar um estado global. O Redux requer três partes principais de construção para funcionar: actions, reducers e store.

Actions

Actions são objetos usados ​​para enviar dados para o armazenamento Redux. Eles normalmente têm duas propriedades: uma propriedade de tipo para descrever o que a ação faz e uma propriedade de carga útil que contém as informações que devem ser alteradas no estado da aplicação:

// action.js
const reduxAction = payload => {
  return {
    type: 'action description',
    payload
  }
};

export default reduxAction;
Enter fullscreen mode Exit fullscreen mode

O tipo é geralmente em letras maiúsculas com suas palavras separadas por sublinhados. Por exemplo, SIGNUP_USER ou DELETE_USER_DATA.

Reducers

Os reducers são funções puras que implementam o comportamento da ação. Eles pegam o estado atual da aplicação, executam uma ação e então retornam um novo estado:

const reducer = (state, action) => {
  const { type, payload } = action;
  switch(type){
    case "action type":
      return {
        ["action description"]: payload
      };
    default:
      return state;
  }
};

export default reducer;
Enter fullscreen mode Exit fullscreen mode

Store

O estado da aplicação é armazenado na store. Existe apenas uma store em qualquer aplicação Redux:

import { createStore } from 'redux'

const store = createStore(componentName);
Enter fullscreen mode Exit fullscreen mode

Como nossa aplicação pode ter apenas um armazenamento Redux, para criar um único reducer raiz para todos os nossos componentes, precisaremos do método combineReducers do Redux.

Com a quantidade considerável de código necessária para configurar o Redux, imagine como seria nossa base de código quando tivéssemos vários componentes para trabalhar.

Embora o Redux resolva nosso problema de gerenciamento de estado, é realmente demorado de usar, tem uma curva de aprendizado difícil e introduz uma nova camada de complexidade em nossa aplicação.

Felizmente, a API React Context resolve esse problema. Quando combinado com React Hooks, temos uma solução de gerenciamento de estado que consome menos tempo para configurar, tem uma curva de aprendizado mais fácil e requer o mínimo de código.

O que é a API React Context?

A nova API de contexto foi introduzida no React v16.3. O React Context permite que você compartilhe dados que podem ser considerados globais para uma árvore de componentes React, como o usuário autenticado atual, tema ou idioma preferencial.

De acordo com a documentação do React , o Context fornece uma maneira de passar dados pela árvore de componentes sem ter que passar props manualmente em todos os níveis. Essencialmente, a API React Context é a maneira do React de gerenciar o estado em vários componentes que não estão diretamente conectados.

Para criar um contexto, usaremos o método createContext do React, que aceita um parâmetro como seu valor padrão:

import React, {createContext} from 'react';

const newContext = createContext({ color: 'black' });
Enter fullscreen mode Exit fullscreen mode

O método createContext, retorna um objeto com um componente Provider e um Consumer:

const { Provider, Consumer } = newContext;
Enter fullscreen mode Exit fullscreen mode

O componente Provider torna o estado disponível para todos os componentes filhos, não importa o quão profundamente aninhados eles estejam dentro da hierarquia do componente. O componente Provider recebe uma propriedade de valor, que é onde passaremos nosso valor atual:

<Provider value={color: 'blue'}>
  {children}
</Provider>
Enter fullscreen mode Exit fullscreen mode

O Consumer, como o próprio nome indica, consome os dados do Provider sem nenhuma necessidade de prop drill:

<Consumer>
  {value => <span>{value}</span>}}
</Consumer>
Enter fullscreen mode Exit fullscreen mode

Sem Hooks, a Context API pode não parecer muito quando comparada ao Redux. Mas, quando combinado com o hook useReducer, temos uma solução que finalmente resolve o problema de gerenciamento de estado no React.

O Hook useContext

Você deve ter notado que, ao descrever a API React Context, precisávamos envolver nosso conteúdo em um componente Consumer e, em seguida, passar uma função como filho para que pudéssemos acessar ou consumir nosso estado.

Fazer isso introduz um aninhamento desnecessário de componentes e aumenta a complexidade do nosso código.

O hook useContext torna as coisas muito mais limpas e diretas. Para acessar nosso estado com o hook useContext, basta chamá-lo com nosso contexto criado como argumento:

const newContext = React.createContext({ color: 'black' });

const value = useContext(newContext);

console.log(value); // this will return { color: 'black' }
Enter fullscreen mode Exit fullscreen mode

Agora, em vez de envolver nosso conteúdo em um componente Consumer, podemos simplesmente acessar nosso estado por meio da variável value.

O Hook useReducer

O hook useReducer veio com o React v16.8. Assim como o método reduce() em JavaScript, o hook useReducer recebe dois valores como seu argumento, uma função reducer e um estado inicial. Em seguida, ele retorna um novo estado:

const [state, dispatch] = useReducer((state, action) => {
  const { type } = action;
  switch(action) {
    case 'action description':
      const newState = // do something with the action
      return newState;
    default:
      throw new Error()
  }
}, []);
Enter fullscreen mode Exit fullscreen mode

No bloco de código acima, definimos nosso estado e um método correspondente, dispatch, para tratá-lo. Ao chamarmos o método dispatch, o Hook useReducer() realizará uma ação baseada no tipo que nosso método recebe em seu argumento action:

...
return (
  <button onClick={() =>
    dispatch({ type: 'action type'})}>
  </button>
)
Enter fullscreen mode Exit fullscreen mode

Como usar o hook useReducer com React Context

Agora que sabemos como a Context API e o Hook useReducer funcionam individualmente, vamos ver o que acontece quando os combinamos para obter a solução de gerenciamento de estado global ideal para nossa aplicação.

// store.js
import React, {createContext, useReducer} from 'react';

const initialState = {};
const store = createContext(initialState);
const { Provider } = store;

const StateProvider = ( { children } ) => {
  const [state, dispatch] = useReducer((state, action) => {
    switch(action.type) {
      case 'action description':
        const newState = // do something with the action
        return newState;
      default:
        throw new Error();
    };
  }, initialState);

  return <Provider value={{ state, dispatch }}>{children}</Provider>;
};

export { store, StateProvider }
Enter fullscreen mode Exit fullscreen mode

Em nosso arquivo store.js, usamos o método createContext() do React para criar um novo contexto.

Lembre-se que o método createContext() retorna um objeto com um componente Provider e Consumer. Desta vez, usaremos apenas o componente Provider e o hook useContext quando precisarmos acessar nosso estado.

Observe como usamos o hook useReducer em nosso StateProvider. Quando precisarmos manipular nosso estado, chamaremos o método dispatch e passaremos um objeto com o tipo desejado como argumento.

Em nosso StateProvider, retornamos nosso componente Provider com uma propriedade de valor de estado e envio do hook useReducer.

Acessando o estado globalmente

Para acessar nosso estado globalmente, precisaremos agrupar nosso componente raiz <App/> em nosso StoreProvider antes de renderizá-lo em nossa função ReactDOM.render():

// root index.js file
import { createRoot } from "react-dom/client";
import App from './App';
import { StateProvider } from './store.js';

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StateProvider>
    <App />
  </StateProvider>
);
Enter fullscreen mode Exit fullscreen mode

Agora, nosso contexto de armazenamento pode ser acessado de qualquer componente na árvore de componentes. Para fazer isso, importaremos o hook useContext do React e a store do nosso arquivo ./store.js:

// exampleComponent.js
import React, { useContext } from 'react';
import { store } from './store.js';

const ExampleComponent = () => {
  const globalState = useContext(store);
  console.log(globalState); // this will return { color: red }
};
Enter fullscreen mode Exit fullscreen mode

Adicionando e removendo dados do estado

Vimos como podemos acessar nosso estado global. Para adicionar e remover dados de nosso estado, precisaremos do método dispatch de nosso contexto de armazenamento. Precisamos apenas chamar o método dispatch e passar em um objeto com type como parâmetro, a descrição da ação conforme definido em nosso componente StateProvider:

// exampleComponent.js
import React, { useContext } from 'react';
import { store } from './store.js';

const ExampleComponent = () => {
  const globalState = useContext(store);
  const { dispatch } = globalState;

  dispatch({ type: 'action description' })
};
Enter fullscreen mode Exit fullscreen mode

Conclusão

Neste tutorial, exploramos as diferenças entre usar o Redux para gerenciamento de estado em aplicações React e usar a API React Context junto com o Hook useContext e o Hook useReducer. Quando usamos o Redux para gerenciamento de estado, temos que lidar com a perfuração de suporte, o que significa que temos que escrever muito código extra apenas para iniciar nossa aplicação. Usando Context para aninhar componentes dentro de componentes, toda a funcionalidade do componente pai está disponível nos componentes filhos.

Espero que tenha gostado deste artigo, e não se esqueça de deixar um comentário se tiver alguma dúvida.

Top comments (0)