DEV Community

Cover image for Por que eu não uso bibliotecas de gerenciamento de estado no React
Eduardo Rabelo
Eduardo Rabelo

Posted on • Originally published at oieduardorabelo.hashnode.dev on

Por que eu não uso bibliotecas de gerenciamento de estado no React

Original source in English by Fabrizio Beccaceci
Why i no longer use a React state management library
https://medium.com/@ipla/why-i-no-longer-use-a-react-state-management-library-7bdffae54600

Índice

  • O que é uma Biblioteca de Gerenciamento de Estado Global?
  • Como o Redux Toolkit Resolve o Estado Global
  • As Duas Faces do Estado Global
  • A Abordagem Enxuta para Estado Global
    • Para Estado do Servidor use TanStack Query
    • Para Estado Compartilhado use Observables
  • Por que não usar React Context?
  • Conclusão

Quando comecei a aprender React anos atrs, havia duas coisas que voc precisava saber para se considerar um desenvolvedor React: TypeScript e Redux.

Hoje em dia, as coisas mudaram. Se voc est aprendendo React agora e se depara com o conceito de gerenciamento de estado, vai se sentir sobrecarregado com uma infinidade de bibliotecas: Redux, Redux Toolkit, MobX, Jotai, Zustand e a lista continua.

Mas antes de nos aprofundarmos, vamos responder pergunta bsica:

O que uma Biblioteca de Gerenciamento de Estado Global?

Mesmo se voc for relativamente novo no React, provavelmente sabe o que o termo estado. Voc cria um com o hook useState, e quando voc o atualiza, seu aplicativo renderiza novamente para refletir as mudanas.

No entanto, as coisas ficam complicadas quando voc precisa compartilhar alguma informao entre vrias partes no conectadas do seu aplicativo React.

Isso o que frequentemente chamamos de estado global , e o problema que as bibliotecas de gerenciamento de estado tentam resolver: como tornar o estado compartilhado acessvel a qualquer componente.

Vamos ver como o Redux Toolkit lida com isso.

Como o Redux Toolkit Resolve o Estado Global

Primeiro, voc cria uma store :

import { configureStore } from '@reduxjs/toolkit';

export const store = configureStore({
  reducer: {},
});

// Inferindo os tipos de RootState e AppDispatch usando a store
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Enter fullscreen mode Exit fullscreen mode

Este o local central onde seu estado global ir existir. Em seguida, voc envolve seu aplicativo com um provider para tornar a store acessvel a todos os componentes:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import { store } from './app/store';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

Agora, voc precisa definir os slices , que so partes individuais do seu estado global. Por exemplo:

import { createSlice } from '@reduxjs/toolkit';

interface CounterState {
  value: number;
}
const initialState: CounterState = { value: 0 };
export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => { state.value += 1; },
    decrement: (state) => { state.value -= 1; },
    incrementByAmount: (state, action) => {
      state.value += action.payload;
    },
  },
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
Enter fullscreen mode Exit fullscreen mode

Por fim, voc conecta este slicestore:

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './features/counter/counterSlice';

export const store = configureStore({
  reducer: { counter: counterReducer },
});
Enter fullscreen mode Exit fullscreen mode

Neste ponto, voc pode usar seu estado global nos componentes da seguinte forma:

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './counterSlice';

export function Counter() {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();
  return (
    <div>
      <button onClick={() => dispatch(increment())}>Increment</button>
      <span>{count}</span>
      <button onClick={() => dispatch(decrement())}>Decrement</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Embora o Redux Toolkit simplifique significativamente o Redux, ainda tem muito cdigo boilerplate para o que geralmente uma necessidade simples. E nem sequer falamos sobre aes assncronas como requisies ao servidor!

Alm disso, muitos iniciantes caem na armadilha de colocar todo o seu estado no Redux, levando a stores globais infladas que so mais difceis de gerenciar.

As Duas Faces do Estado Global

Quando voc para pra pensar, a maioria do "estado global" se enquadra em duas categorias:

  1. Estado do Servidor : Dados obtidos de um servidor, como uma lista de clientes em um aplicativo CRM.

  2. Estado da UI Compartilhado : Pequenos pedaos de dados necessrios em vrios lugares, como o usurio atualmente logado.

Vamos abordar cada um deles separadamente.

A Abordagem Enxuta para Estado Global

Com ferramentas modernas, voc pode lidar com estado global sem uma biblioteca dedicada de gerenciamento de estado:

1. Para Estado do Servidor use TanStack Query

TanStack Query (anteriormente React Query) feito especificamente para gerenciar estado do servidor. Ele lida com cache, recarregamento, dados obsoletos e muito mais, tudo pronto para uso, e voc definitivamente deveria usar isso porque voc realmente no vai querer reimplementar tudo isso do zero. Por exemplo, buscar uma lista de clientes to simples quanto:

import { useQuery } from '@tanstack/react-query';

function Customers() {
  const { data, error, isLoading } = useQuery(['customers'], fetchCustomers);
  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  return <ul>{data.map((customer) => <li key={customer.id}>{customer.name}</li>)}</ul>;
}
Enter fullscreen mode Exit fullscreen mode

O TanStack Query elimina a necessidade de gerenciar manualmente o estado do servidor e, como o estado que voc busca armazenado em cache, voc pode us-lo exatamente da mesma maneira em todos os componentes e ele ser buscado apenas uma vez.

2. Para Estado Compartilhado use Observables

Para estados menores e especficos do aplicativo, como o usurio logado, eu uso uma implementao leve do padro Observable :

export class Observable<T> {
  private _value: T;
  private subscribers = new Set<(value: T) => void>();

  constructor(initialValue: T) {
      this._value = initialValue;
    }
    get value() {
      return this._value;
    }
    set value(newValue: T) {
      this._value = newValue;
      this.notify();
    }
    subscribe(callback: (value: T) => void) {
      this.subscribers.add(callback);
      callback(this._value);
      return { unsubscribe: () => this.subscribers.delete(callback) };
    }
    private notify() {
      this.subscribers.forEach((callback) => callback(this._value));
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Com isso, voc pode conectar Observables ao React usando useSyncExternalStore:

import { useSyncExternalStore } from 'react';

export function useObservable<T>(observable: Observable<T>) {
  return useSyncExternalStore(
    (callback) => observable.subscribe(callback).unsubscribe,
    () => observable.value
  );
}
Enter fullscreen mode Exit fullscreen mode

Agora, voc pode criar e usar observables desta forma:

const loggedUser = new Observable<User>(user);

loggedUser.value // Para acessar o valor dentro do Observable fora do React

loggedUser.value = someOtherUser // Para definir o valor, tanto fora quanto dentro do React
Enter fullscreen mode Exit fullscreen mode

Dentro de um componente React:

const user = useObservable(loggedUser);
Enter fullscreen mode Exit fullscreen mode

Por que no usar React Context?

Embora o React Context funcione em alguns casos, ele tem desvantagens:

  1. Escopo Limitado : Voc no pode acessar o Context fora dos componentes React.

  2. Problemas de Performance : Context dispara uma nova renderizao para todos os componentes abaixo dele, at mesmo para os componentes que no usem o valor atualizado.

Usando Observables evitamos ambos problemas!

Concluso

Gerenciamento de estado global no precisa ser complicado. Combinando TanStack Query e um padro Observable leve, voc pode simplificar seu aplicativo evitando as armadilhas das bibliotecas tradicionais de gerenciamento de estado.

Entre em contato comigo se quiser discutir sobre React, React Native, Nextjs

Bom desenvolvimento! 🎉

Top comments (0)