O que são bibliotecas de gerenciamento de estado e quando você deve usá-las?

À medida que seu app cresce, gerenciar como os dados fluem entre os componentes pode se tornar complexo. Ao começar, o hook useState do React pode ser suficiente, mas à medida que você adiciona recursos, pode encontrar problemas com:
  • Passar props por componentes que não precisam deles, também conhecido como prop drilling
  • Manter os dados sincronizados entre diferentes partes do seu app
  • Manipulando atualizações complexas que afetam múltiplos componentes simultaneamente
Esses e outros desafios podem surgir, o que pode levar a uma base de código mais difícil de manter, depurar e testar. É aí que entram as bibliotecas de gerenciamento de estado – elas fornecem um local centralizado onde os componentes podem obter ou atualizar os dados de que precisam. Vamos dar uma olhada em algumas opções diferentes de gerenciamento de estado que você tem e quando usá-las. A Context API é um gerenciador de estado integrado ao React que permite compartilhar estado entre componentes sem usar uma biblioteca de terceiros. É uma atualização bem estabelecida em relação ao hook useState, então é perfeita para casos como alternância de tema ou status de autenticação do usuário. No entanto, a Context API não lida bem com atualizações frequentes e pode causar re-renderizações desnecessárias, tornando-a menos adequada para necessidades complexas de estado em aplicações como eCommerce e plataformas de mídia social.  Aqui está um componente counter que demonstra o uso básico da Context API:
import { useState, createContext } from 'react';

const CounterContext = createContext();

const CounterProvider = ({ children }) => {
  const [count, setCount] = useState(0);

  return (
    <CounterContext.Provider value={{ count, setCount }}>
      {children}
    </CounterContext.Provider>
  );
};

export { CounterContext, CounterProvider };
Este código cria um contexto e um provider para compartilhar um estado count em toda a aplicação. CounterProvider usa o hook useState para inicializar e gerenciar o estado count e seu setter. Ambos são então passados para componentes filhos através do Provider. Então, quando você envolve seu aplicativo inteiro com o CounterProvider, o estado count fica disponível em toda a sua aplicação. Veja como você pode envolver CounterProvider ao redor da sua aplicação:
import { CounterProvider } from './context/CounterContext';

function App() {
  return (
    <CounterProvider>
        {/* App components */}
    </CounterProvider>
  );
}

export default App;
E aqui está como você pode usar o estado count:
import React, { useContext } from 'react';
import { CounterContext } from '../context/CounterContext';

const Counter = () => {
  const { count, setCount } = useContext(CounterContext);

  return (
    <>
      <div style={{ textAlign: 'center' }}>
        <h1>Context API Counter</h1>
        <button style={{ marginRight: '5px' }} onClick={() => setCount(count - 1)}>
          Decrease
        </button>
        <span>{count}</span>
        <button style={{ marginLeft: '5px' }} onClick={() => setCount(count + 1)}>
          Increase
        </button>
      </div>
    </>
  );
};

export default Counter;
Como você pode ver, o count e sua função setter, setCount, são inicializados através da função useContext. O estado atual count é então exibido, e setCount é usado para aumentar e diminuir o estado count quando o usuário clica nos botões de decremento e incremento respectivamente. Outra biblioteca popular de gerenciamento de estado é o Redux, que é uma das bibliotecas de gerenciamento de estado mais populares para usar com React. Está por aí há muito tempo e é ideal para aplicações maiores como eCommerce e plataformas de mídia social, fóruns e assim por diante. Redux gerencia o estado fornecendo uma store central e controle rigoroso sobre as atualizações de estado. Ele usa um padrão previsível com actions, reducers e middleware. Ações são cargas úteis de informação que enviam dados da sua aplicação para o Redux store, frequentemente acionadas por interações do usuário. Reducers são funções que especificam como o estado deve mudar em resposta a essas ações, garantindo que o estado seja atualizado de forma imutável. Middleware, por outro lado, atua como uma ponte entre o despacho de ações e o reducer, permitindo que você estenda a funcionalidade do Redux (por exemplo, logging, handling async operations) sem modificar o fluxo principal. A reclamação mais comum sobre Redux é todo o código boilerplate que você precisa para começar. Em resposta, a equipe do Redux introduziu "Redux Toolkit" e "RTK Query", que simplificam bastante o processo de configuração. Normalmente você define tanto as actions quanto os reducers em um único arquivo usando a função createSlice(). É comum nomear o arquivo para que termine com a palavra Slice, por exemplo, productSlice, userSlice, counterSlice e assim por diante. Aqui está um arquivo counterSlice para mostrar o básico:
import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',

  initialState: { count: 0 },

  reducers: {
    increment: (state) => {
      state.count += 1;
    },
    decrement: (state) => {
      state.count -= 1;
    },
  },
});

export const { increment, decrement } = counterSlice.actions;

export default counterSlice.reducer;
A partir daqui, você precisa envolver todo o app com o Provider, selecionar uma parte do estado do slice com useSelector(), e então usar useDispatch() para ativar o estado. Outra opção a considerar é Zustand. Zustand é uma biblioteca leve de gerenciamento de estado com uma API simples. É baseado em hooks, então há menos boilerplate comparado ao Redux, tornando mais fácil e rápido de configurar. Zustand é ideal para aplicações de pequeno a médio porte. It works by using a useStore hook to access state directly in components and pages. Isso permite que você modifique e acesse dados sem precisar de actions, reducers ou um provider. Aqui está um useCounterStore que implementa outra funcionalidade de contador:
import { create } from 'zustand';

const useCounterStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}));

export default useCounterStore;
E aqui está como inicializar e usar os estados no seu app:
// Import the useCounterStore (it's just a hook)
import useCounterStore from '../useCounterStore';

const Counter = () => {
  // Initialize the states with the useCounterStore hook
  const { count, increment, decrement } = useCounterStore();

  return (
    <>
      <div style={{ textAlign: 'center' }}>
        <h1>Zustand Counter</h1>
        <button style={{ marginRight: '5px' }} onClick={() => decrement()}>
          Decrease
        </button>
        <span>{count}</span>
        <button style={{ marginLeft: '5px' }} onClick={() => increment()}>
          Increase
        </button>
      </div>
    </>
  );
};

export default Counter;
Embora o ecossistema de front-end esteja em constante evolução e novas bibliotecas de gerenciamento de estado surjam regularmente, as que discutimos são amplamente usadas na indústria.
Este módulo não possui perguntas. Marque como concluído.