Saltar para o conteúdo
Estado Global no React Native: Context vs Zustand vs Redux
Mobile

Estado Global no React Native: Context vs Zustand vs Redux

20 de maio de 2026·Paulo Pereira

O Problema do Estado Global

Passar props através de 5 componentes para chegar onde precisa (prop drilling) é o sinal de que você precisa de estado global. No React Native, as opções são:

  1. Context API — nativa do React, sem dependência
  2. Zustand — leve, simples, performático
  3. Redux Toolkit — verboso, mas poderoso para apps complexos
  4. Jotai/Recoil — estado atômico

Context API: Quando Usar

Ótimo para estado que muda raramente: tema, usuário autenticado, idioma.

// contexts/AuthContext.tsx
import { createContext, useContext, useState, useCallback, ReactNode } from 'react';

type Usuario = { id: number; nome: string; email: string };

type AuthContextType = {
  usuario: Usuario | null;
  autenticar: (email: string, senha: string) => Promise<void>;
  sair: () => void;
};

const AuthContext = createContext<AuthContextType | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
  const [usuario, setUsuario] = useState<Usuario | null>(null);

  const autenticar = useCallback(async (email: string, senha: string) => {
    const resposta = await api.post('/auth/login', { email, senha });
    setUsuario(resposta.data.usuario);
  }, []);

  const sair = useCallback(() => {
    setUsuario(null);
  }, []);

  return (
    <AuthContext.Provider value={{ usuario, autenticar, sair }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error('useAuth deve ser usado dentro de AuthProvider');
  return ctx;
}

Problema do Context: qualquer mudança no valor re-renderiza TODOS os consumidores. Para estado que muda frequentemente (carrinho, contador, preferências), use Zustand.

Zustand: A Escolha Para a Maioria dos Apps

npm install zustand
// stores/carrinhoStore.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';

type Produto = { id: number; nome: string; preco: number };
type ItemCarrinho = Produto & { quantidade: number };

type CarrinhoState = {
  itens: ItemCarrinho[];
  totalItens: number;
  totalPreco: number;
  adicionarItem: (produto: Produto) => void;
  removerItem: (id: number) => void;
  atualizarQuantidade: (id: number, quantidade: number) => void;
  limpar: () => void;
};

export const useCarrinhoStore = create<CarrinhoState>()(
  persist(
    (set, get) => ({
      itens: [],
      totalItens: 0,
      totalPreco: 0,

      adicionarItem: (produto) =>
        set((state) => {
          const existente = state.itens.find((i) => i.id === produto.id);
          const itens = existente
            ? state.itens.map((i) =>
                i.id === produto.id ? { ...i, quantidade: i.quantidade + 1 } : i
              )
            : [...state.itens, { ...produto, quantidade: 1 }];

          return {
            itens,
            totalItens: itens.reduce((s, i) => s + i.quantidade, 0),
            totalPreco: itens.reduce((s, i) => s + i.preco * i.quantidade, 0),
          };
        }),

      removerItem: (id) =>
        set((state) => {
          const itens = state.itens.filter((i) => i.id !== id);
          return {
            itens,
            totalItens: itens.reduce((s, i) => s + i.quantidade, 0),
            totalPreco: itens.reduce((s, i) => s + i.preco * i.quantidade, 0),
          };
        }),

      atualizarQuantidade: (id, quantidade) =>
        set((state) => ({
          itens: state.itens.map((i) =>
            i.id === id ? { ...i, quantidade } : i
          ),
        })),

      limpar: () => set({ itens: [], totalItens: 0, totalPreco: 0 }),
    }),
    {
      name: 'carrinho-storage',
      storage: createJSONStorage(() => AsyncStorage),
    }
  )
);

Usando no componente:

function BotaoCarrinho() {
  // Seleciona apenas o que precisa — sem re-render desnecessário
  const totalItens = useCarrinhoStore((s) => s.totalItens);
  const adicionarItem = useCarrinhoStore((s) => s.adicionarItem);

  return (
    <TouchableOpacity onPress={() => adicionarItem(produto)}>
      <Text>Carrinho ({totalItens})</Text>
    </TouchableOpacity>
  );
}

Seletores e Performance

// Crie seletores reutilizáveis
const selecionarItensPorCategoria = (categoria: string) =>
  useCarrinhoStore((s) => s.itens.filter((i) => i.categoria === categoria));

// Shallow comparison para objetos — evita re-renders
import { useShallow } from 'zustand/react/shallow';

function ResumoCarrinho() {
  const { totalItens, totalPreco } = useCarrinhoStore(
    useShallow((s) => ({ totalItens: s.totalItens, totalPreco: s.totalPreco }))
  );
}

Redux Toolkit: Quando Vale a Pena

Use Redux para apps enterprise com:

  • Time grande (padrão estabelecido)
  • Estado muito complexo com muitas relações
  • Necessidade de time-travel debugging
  • Cache de API com RTK Query
// store/pedidosSlice.ts
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

export const buscarPedidos = createAsyncThunk('pedidos/buscar', async (usuarioId: number) => {
  const resp = await api.get(`/usuarios/${usuarioId}/pedidos`);
  return resp.data;
});

const pedidosSlice = createSlice({
  name: 'pedidos',
  initialState: { itens: [], carregando: false, erro: null as string | null },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(buscarPedidos.pending, (state) => { state.carregando = true; })
      .addCase(buscarPedidos.fulfilled, (state, action) => {
        state.carregando = false;
        state.itens = action.payload;
      })
      .addCase(buscarPedidos.rejected, (state, action) => {
        state.carregando = false;
        state.erro = action.error.message ?? 'Erro desconhecido';
      });
  },
});

Guia de Decisão

CenárioSolução
Auth, tema, idiomaContext API
Carrinho, preferências, UI stateZustand
App enterprise com time grandeRedux Toolkit
Estado derivado complexoJotai
Cache de dados de APIReact Query + Zustand

Conclusão

Para a maioria dos apps React Native em 2026, a combinação Context para auth/tema + Zustand para estado de negócio cobre tudo com simplicidade e performance. Redux só vale a pena quando a complexidade do projeto justifica a verbosidade.