Gerenciamento de estado com Context API em React Native (padrão para apps pequenos e médios)

Capítulo 7

Tempo estimado de leitura: 16 minutos

+ Exercício

O que é Context API e quando usar

A Context API é um mecanismo do React para compartilhar dados “globais” (ou amplamente reutilizados) entre componentes sem precisar passar props por muitas camadas. Em apps React Native pequenos e médios, ela costuma ser um padrão suficiente para: autenticação (usuário logado), preferências (tema/idioma), carrinho simples, feature flags e configurações de app.

O ponto central: quando o valor de um Context muda, todos os componentes que consomem esse Context podem re-renderizar. Por isso, além de criar o Context, é importante desenhar a arquitetura para reduzir renders desnecessários (por exemplo, separando Context de estado e Context de ações).

Boas práticas que vamos aplicar

  • Provider único por domínio (ex.: Auth, Preferences, Cart) ou um Provider raiz que compõe vários.
  • useReducer para centralizar atualizações e evitar “setState espalhado”.
  • Estado derivado calculado com useMemo (ex.: total do carrinho, se está autenticado).
  • Separar Context de estado e Context de ações para reduzir renders em componentes que só disparam ações.
  • Persistência + reidratação na inicialização para manter sessão e preferências.

Estrutura sugerida de pastas (foco em Context)

Uma organização simples e escalável é agrupar por “domínio”:

src/contexts/  auth/    AuthProvider.tsx    authReducer.ts    authStorage.ts    types.ts  preferences/    PreferencesProvider.tsx    preferencesReducer.ts    preferencesStorage.ts    types.ts  cart/    CartProvider.tsx    cartReducer.ts    cartStorage.ts    types.ts  AppProviders.tsx

Isso evita um “Context gigante” e mantém responsabilidades claras.

Persistência: AsyncStorage e reidratação

Para persistir estado localmente em React Native, uma opção comum é @react-native-async-storage/async-storage. A ideia é: ao iniciar o app, ler o estado salvo (rehydrate) e popular o reducer; e, sempre que o estado mudar, salvar novamente (persist).

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

Instalação (referência):

npm i @react-native-async-storage/async-storage

Exemplo real: Auth + Preferências + Carrinho

A seguir, vamos montar três domínios com Context + Provider, cada um com: reducer, ações, estado derivado e persistência.

1) Autenticação (usuário autenticado)

Tipos e estado inicial

// src/contexts/auth/types.ts
export type User = {
  id: string;
  name: string;
  email: string;
};

export type AuthState = {
  user: User | null;
  token: string | null;
  isHydrated: boolean; // indica se já reidratou do storage
};

export type AuthAction =
  | { type: 'HYDRATE'; payload: { user: User | null; token: string | null } }
  | { type: 'SIGN_IN_SUCCESS'; payload: { user: User; token: string } }
  | { type: 'SIGN_OUT' };

export const authInitialState: AuthState = {
  user: null,
  token: null,
  isHydrated: false,
};

Reducer simples

// src/contexts/auth/authReducer.ts
import { AuthAction, AuthState, authInitialState } from './types';

export function authReducer(state: AuthState, action: AuthAction): AuthState {
  switch (action.type) {
    case 'HYDRATE':
      return {
        ...state,
        user: action.payload.user,
        token: action.payload.token,
        isHydrated: true,
      };
    case 'SIGN_IN_SUCCESS':
      return {
        ...state,
        user: action.payload.user,
        token: action.payload.token,
      };
    case 'SIGN_OUT':
      return { ...authInitialState, isHydrated: true };
    default:
      return state;
  }
}

Storage (persist/rehydrate)

// src/contexts/auth/authStorage.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import { User } from './types';

const AUTH_KEY = '@app/auth';

type PersistedAuth = {
  user: User | null;
  token: string | null;
};

export async function loadAuth(): Promise<PersistedAuth> {
  const raw = await AsyncStorage.getItem(AUTH_KEY);
  if (!raw) return { user: null, token: null };
  try {
    return JSON.parse(raw) as PersistedAuth;
  } catch {
    return { user: null, token: null };
  }
}

export async function saveAuth(data: PersistedAuth): Promise<void> {
  await AsyncStorage.setItem(AUTH_KEY, JSON.stringify(data));
}

export async function clearAuth(): Promise<void> {
  await AsyncStorage.removeItem(AUTH_KEY);
}

Separando Context de estado e de ações

Criaremos dois contexts: um para estado e outro para ações. Assim, um componente que só chama signOut() não precisa re-renderizar quando o usuário muda (desde que ele não consuma o estado).

// src/contexts/auth/AuthProvider.tsx
import React, { createContext, useCallback, useContext, useEffect, useMemo, useReducer } from 'react';
import { authReducer } from './authReducer';
import { AuthState, authInitialState, User } from './types';
import { clearAuth, loadAuth, saveAuth } from './authStorage';

type AuthActions = {
  signIn: (email: string, password: string) => Promise<void>;
  signOut: () => Promise<void>;
};

const AuthStateContext = createContext<AuthState | null>(null);
const AuthActionsContext = createContext<AuthActions | null>(null);

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [state, dispatch] = useReducer(authReducer, authInitialState);

  // Reidratação na inicialização
  useEffect(() => {
    let mounted = true;
    (async () => {
      const persisted = await loadAuth();
      if (!mounted) return;
      dispatch({ type: 'HYDRATE', payload: persisted });
    })();
    return () => {
      mounted = false;
    };
  }, []);

  // Persistência sempre que user/token mudarem (após hidratar)
  useEffect(() => {
    if (!state.isHydrated) return;
    saveAuth({ user: state.user, token: state.token });
  }, [state.user, state.token, state.isHydrated]);

  const signIn = useCallback(async (email: string, password: string) => {
    // Exemplo didático: simular login. Em app real, chamar API.
    await new Promise((r) => setTimeout(r, 400));
    const fakeUser: User = { id: 'u1', name: 'Ada Lovelace', email };
    const fakeToken = 'token_abc123';
    dispatch({ type: 'SIGN_IN_SUCCESS', payload: { user: fakeUser, token: fakeToken } });
  }, []);

  const signOut = useCallback(async () => {
    dispatch({ type: 'SIGN_OUT' });
    await clearAuth();
  }, []);

  const actions = useMemo<AuthActions>(() => ({ signIn, signOut }), [signIn, signOut]);

  return (
    <AuthStateContext.Provider value={state}>
      <AuthActionsContext.Provider value={actions}>
        {children}
      </AuthActionsContext.Provider>
    </AuthStateContext.Provider>
  );
}

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

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

// Estado derivado (ex.: isAuthenticated)
export function useIsAuthenticated() {
  const { token } = useAuthState();
  return Boolean(token);
}

2) Preferências (tema e idioma)

Preferências são um ótimo caso para Context. Vamos manter theme e language, com ações para alternar e persistência.

// src/contexts/preferences/types.ts
export type ThemeMode = 'light' | 'dark';
export type Language = 'pt-BR' | 'en-US';

export type PreferencesState = {
  theme: ThemeMode;
  language: Language;
  isHydrated: boolean;
};

export type PreferencesAction =
  | { type: 'HYDRATE'; payload: { theme: ThemeMode; language: Language } }
  | { type: 'SET_THEME'; payload: ThemeMode }
  | { type: 'SET_LANGUAGE'; payload: Language };

export const preferencesInitialState: PreferencesState = {
  theme: 'light',
  language: 'pt-BR',
  isHydrated: false,
};
// src/contexts/preferences/preferencesReducer.ts
import { PreferencesAction, PreferencesState } from './types';

export function preferencesReducer(
  state: PreferencesState,
  action: PreferencesAction
): PreferencesState {
  switch (action.type) {
    case 'HYDRATE':
      return { ...state, ...action.payload, isHydrated: true };
    case 'SET_THEME':
      return { ...state, theme: action.payload };
    case 'SET_LANGUAGE':
      return { ...state, language: action.payload };
    default:
      return state;
  }
}
// src/contexts/preferences/preferencesStorage.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Language, ThemeMode } from './types';

const PREFS_KEY = '@app/preferences';

type PersistedPrefs = { theme: ThemeMode; language: Language };

export async function loadPreferences(): Promise<PersistedPrefs> {
  const raw = await AsyncStorage.getItem(PREFS_KEY);
  if (!raw) return { theme: 'light', language: 'pt-BR' };
  try {
    return JSON.parse(raw) as PersistedPrefs;
  } catch {
    return { theme: 'light', language: 'pt-BR' };
  }
}

export async function savePreferences(data: PersistedPrefs): Promise<void> {
  await AsyncStorage.setItem(PREFS_KEY, JSON.stringify(data));
}
// src/contexts/preferences/PreferencesProvider.tsx
import React, { createContext, useCallback, useContext, useEffect, useMemo, useReducer } from 'react';
import { preferencesReducer } from './preferencesReducer';
import { preferencesInitialState, PreferencesState, ThemeMode, Language } from './types';
import { loadPreferences, savePreferences } from './preferencesStorage';

type PreferencesActions = {
  setTheme: (mode: ThemeMode) => void;
  toggleTheme: () => void;
  setLanguage: (lang: Language) => void;
};

const PreferencesStateContext = createContext<PreferencesState | null>(null);
const PreferencesActionsContext = createContext<PreferencesActions | null>(null);

export function PreferencesProvider({ children }: { children: React.ReactNode }) {
  const [state, dispatch] = useReducer(preferencesReducer, preferencesInitialState);

  useEffect(() => {
    let mounted = true;
    (async () => {
      const persisted = await loadPreferences();
      if (!mounted) return;
      dispatch({ type: 'HYDRATE', payload: persisted });
    })();
    return () => {
      mounted = false;
    };
  }, []);

  useEffect(() => {
    if (!state.isHydrated) return;
    savePreferences({ theme: state.theme, language: state.language });
  }, [state.theme, state.language, state.isHydrated]);

  const setTheme = useCallback((mode: ThemeMode) => {
    dispatch({ type: 'SET_THEME', payload: mode });
  }, []);

  const toggleTheme = useCallback(() => {
    dispatch({ type: 'SET_THEME', payload: state.theme === 'light' ? 'dark' : 'light' });
  }, [state.theme]);

  const setLanguage = useCallback((lang: Language) => {
    dispatch({ type: 'SET_LANGUAGE', payload: lang });
  }, []);

  const actions = useMemo(() => ({ setTheme, toggleTheme, setLanguage }), [setTheme, toggleTheme, setLanguage]);

  return (
    <PreferencesStateContext.Provider value={state}>
      <PreferencesActionsContext.Provider value={actions}>
        {children}
      </PreferencesActionsContext.Provider>
    </PreferencesStateContext.Provider>
  );
}

export function usePreferencesState() {
  const ctx = useContext(PreferencesStateContext);
  if (!ctx) throw new Error('usePreferencesState deve ser usado dentro de PreferencesProvider');
  return ctx;
}

export function usePreferencesActions() {
  const ctx = useContext(PreferencesActionsContext);
  if (!ctx) throw new Error('usePreferencesActions deve ser usado dentro de PreferencesProvider');
  return ctx;
}

3) Carrinho simples (itens, quantidade, total derivado)

O carrinho é um exemplo clássico de estado global. Vamos manter itens com quantidade, ações para adicionar/remover/limpar e um total derivado.

// src/contexts/cart/types.ts
export type CartItem = {
  id: string;
  title: string;
  price: number;
  quantity: number;
};

export type CartState = {
  items: CartItem[];
  isHydrated: boolean;
};

export type CartAction =
  | { type: 'HYDRATE'; payload: { items: CartItem[] } }
  | { type: 'ADD_ITEM'; payload: { id: string; title: string; price: number } }
  | { type: 'REMOVE_ITEM'; payload: { id: string } }
  | { type: 'SET_QTY'; payload: { id: string; quantity: number } }
  | { type: 'CLEAR' };

export const cartInitialState: CartState = {
  items: [],
  isHydrated: false,
};
// src/contexts/cart/cartReducer.ts
import { CartAction, CartState } from './types';

export function cartReducer(state: CartState, action: CartAction): CartState {
  switch (action.type) {
    case 'HYDRATE':
      return { ...state, items: action.payload.items, isHydrated: true };
    case 'ADD_ITEM': {
      const existing = state.items.find((i) => i.id === action.payload.id);
      if (existing) {
        return {
          ...state,
          items: state.items.map((i) =>
            i.id === action.payload.id ? { ...i, quantity: i.quantity + 1 } : i
          ),
        };
      }
      return {
        ...state,
        items: [
          ...state.items,
          { id: action.payload.id, title: action.payload.title, price: action.payload.price, quantity: 1 },
        ],
      };
    }
    case 'REMOVE_ITEM':
      return { ...state, items: state.items.filter((i) => i.id !== action.payload.id) };
    case 'SET_QTY':
      return {
        ...state,
        items: state.items
          .map((i) => (i.id === action.payload.id ? { ...i, quantity: action.payload.quantity } : i))
          .filter((i) => i.quantity > 0),
      };
    case 'CLEAR':
      return { ...state, items: [] };
    default:
      return state;
  }
}
// src/contexts/cart/cartStorage.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import { CartItem } from './types';

const CART_KEY = '@app/cart';

type PersistedCart = { items: CartItem[] };

export async function loadCart(): Promise<PersistedCart> {
  const raw = await AsyncStorage.getItem(CART_KEY);
  if (!raw) return { items: [] };
  try {
    return JSON.parse(raw) as PersistedCart;
  } catch {
    return { items: [] };
  }
}

export async function saveCart(data: PersistedCart): Promise<void> {
  await AsyncStorage.setItem(CART_KEY, JSON.stringify(data));
}
// src/contexts/cart/CartProvider.tsx
import React, { createContext, useCallback, useContext, useEffect, useMemo, useReducer } from 'react';
import { cartReducer } from './cartReducer';
import { cartInitialState, CartState } from './types';
import { loadCart, saveCart } from './cartStorage';

type CartActions = {
  addItem: (item: { id: string; title: string; price: number }) => void;
  removeItem: (id: string) => void;
  setQty: (id: string, quantity: number) => void;
  clear: () => void;
};

const CartStateContext = createContext<CartState | null>(null);
const CartActionsContext = createContext<CartActions | null>(null);

export function CartProvider({ children }: { children: React.ReactNode }) {
  const [state, dispatch] = useReducer(cartReducer, cartInitialState);

  useEffect(() => {
    let mounted = true;
    (async () => {
      const persisted = await loadCart();
      if (!mounted) return;
      dispatch({ type: 'HYDRATE', payload: persisted });
    })();
    return () => {
      mounted = false;
    };
  }, []);

  useEffect(() => {
    if (!state.isHydrated) return;
    saveCart({ items: state.items });
  }, [state.items, state.isHydrated]);

  const addItem = useCallback((item: { id: string; title: string; price: number }) => {
    dispatch({ type: 'ADD_ITEM', payload: item });
  }, []);

  const removeItem = useCallback((id: string) => {
    dispatch({ type: 'REMOVE_ITEM', payload: { id } });
  }, []);

  const setQty = useCallback((id: string, quantity: number) => {
    dispatch({ type: 'SET_QTY', payload: { id, quantity } });
  }, []);

  const clear = useCallback(() => {
    dispatch({ type: 'CLEAR' });
  }, []);

  const actions = useMemo(() => ({ addItem, removeItem, setQty, clear }), [addItem, removeItem, setQty, clear]);

  return (
    <CartStateContext.Provider value={state}>
      <CartActionsContext.Provider value={actions}>
        {children}
      </CartActionsContext.Provider>
    </CartStateContext.Provider>
  );
}

export function useCartState() {
  const ctx = useContext(CartStateContext);
  if (!ctx) throw new Error('useCartState deve ser usado dentro de CartProvider');
  return ctx;
}

export function useCartActions() {
  const ctx = useContext(CartActionsContext);
  if (!ctx) throw new Error('useCartActions deve ser usado dentro de CartProvider');
  return ctx;
}

// Estado derivado: total e quantidade total
export function useCartSummary() {
  const { items } = useCartState();
  return useMemo(() => {
    const totalItems = items.reduce((acc, i) => acc + i.quantity, 0);
    const totalPrice = items.reduce((acc, i) => acc + i.quantity * i.price, 0);
    return { totalItems, totalPrice };
  }, [items]);
}

Compondo Providers no app

Para não “anilhar” providers no App.tsx de forma desorganizada, crie um componente agregador.

// src/contexts/AppProviders.tsx
import React from 'react';
import { AuthProvider } from './auth/AuthProvider';
import { PreferencesProvider } from './preferences/PreferencesProvider';
import { CartProvider } from './cart/CartProvider';

export function AppProviders({ children }: { children: React.ReactNode }) {
  return (
    <AuthProvider>
      <PreferencesProvider>
        <CartProvider>{children}</CartProvider>
      </PreferencesProvider>
    </AuthProvider>
  );
}

Uso:

// App.tsx
import React from 'react';
import { AppProviders } from './src/contexts/AppProviders';
import { Root } from './src/Root';

export default function App() {
  return (
    <AppProviders>
      <Root />
    </AppProviders>
  );
}

Passo a passo prático: consumindo estado e ações

Tela de perfil (mostra usuário e botão de sair)

Note como o botão de sair pode consumir apenas ações, reduzindo renders quando o estado muda.

// src/screens/ProfileScreen.tsx
import React from 'react';
import { View, Text, Button } from 'react-native';
import { useAuthState, useAuthActions } from '../contexts/auth/AuthProvider';

export function ProfileScreen() {
  const { user, isHydrated } = useAuthState();
  const { signOut } = useAuthActions();

  if (!isHydrated) return <Text>Carregando...</Text>;

  return (
    <View>
      <Text>{user ? `Olá, ${user.name}` : 'Você não está logado'}</Text>
      {user ? <Button title="Sair" onPress={signOut} /> : null}
    </View>
  );
}

Tela de configurações (tema e idioma)

// src/screens/SettingsScreen.tsx
import React from 'react';
import { View, Text, Button } from 'react-native';
import { usePreferencesState, usePreferencesActions } from '../contexts/preferences/PreferencesProvider';

export function SettingsScreen() {
  const { theme, language, isHydrated } = usePreferencesState();
  const { toggleTheme, setLanguage } = usePreferencesActions();

  if (!isHydrated) return <Text>Carregando...</Text>;

  return (
    <View>
      <Text>Tema atual: {theme}</Text>
      <Button title="Alternar tema" onPress={toggleTheme} />

      <Text>Idioma: {language}</Text>
      <Button title="Português" onPress={() => setLanguage('pt-BR')} />
      <Button title="English" onPress={() => setLanguage('en-US')} />
    </View>
  );
}

Lista de produtos (adiciona ao carrinho) + badge com total derivado

// src/components/CartBadge.tsx
import React from 'react';
import { Text } from 'react-native';
import { useCartSummary } from '../contexts/cart/CartProvider';

export function CartBadge() {
  const { totalItems } = useCartSummary();
  return <Text>Carrinho: {totalItems}</Text>;
}
// src/screens/ProductsScreen.tsx
import React from 'react';
import { View, Text, Button } from 'react-native';
import { useCartActions } from '../contexts/cart/CartProvider';

const PRODUCTS = [
  { id: 'p1', title: 'Camiseta', price: 59.9 },
  { id: 'p2', title: 'Caneca', price: 29.9 },
];

export function ProductsScreen() {
  const { addItem } = useCartActions();

  return (
    <View>
      {PRODUCTS.map((p) => (
        <View key={p.id}>
          <Text>{p.title} - R$ {p.price.toFixed(2)}</Text>
          <Button title="Adicionar" onPress={() => addItem(p)} />
        </View>
      ))}
    </View>
  );
}

Tela do carrinho (estado + ações + total)

// src/screens/CartScreen.tsx
import React from 'react';
import { View, Text, Button } from 'react-native';
import { useCartState, useCartActions, useCartSummary } from '../contexts/cart/CartProvider';

export function CartScreen() {
  const { items, isHydrated } = useCartState();
  const { setQty, removeItem, clear } = useCartActions();
  const { totalPrice } = useCartSummary();

  if (!isHydrated) return <Text>Carregando...</Text>;

  return (
    <View>
      {items.length === 0 ? <Text>Carrinho vazio</Text> : null}

      {items.map((i) => (
        <View key={i.id}>
          <Text>{i.title} (x{i.quantity}) - R$ {(i.price * i.quantity).toFixed(2)}</Text>
          <Button title="+" onPress={() => setQty(i.id, i.quantity + 1)} />
          <Button title="-" onPress={() => setQty(i.id, i.quantity - 1)} />
          <Button title="Remover" onPress={() => removeItem(i.id)} />
        </View>
      ))}

      <Text>Total: R$ {totalPrice.toFixed(2)}</Text>
      <Button title="Limpar carrinho" onPress={clear} />
    </View>
  );
}

Reidratação coordenada: evitando “flash” de estado padrão

Quando você persiste estado, é comum o app iniciar com valores padrão e, alguns milissegundos depois, trocar para o estado reidratado. Para evitar esse “flash”, use flags como isHydrated (como fizemos) e só renderize a UI principal quando os domínios críticos estiverem prontos.

Exemplo de gate simples:

// src/Root.tsx
import React from 'react';
import { Text } from 'react-native';
import { useAuthState } from './contexts/auth/AuthProvider';
import { usePreferencesState } from './contexts/preferences/PreferencesProvider';
import { useCartState } from './contexts/cart/CartProvider';

export function Root() {
  const auth = useAuthState();
  const prefs = usePreferencesState();
  const cart = useCartState();

  const ready = auth.isHydrated && prefs.isHydrated && cart.isHydrated;
  if (!ready) return <Text>Inicializando...</Text>;

  return <Text>App pronto</Text>;
}

Alertas de performance: por que separar estado e ações ajuda

  • Se um componente consome apenas ações (ex.: signOut), ele não precisa re-renderizar quando user muda.
  • Se você coloca {state, actions} no mesmo Context, qualquer mudança em state muda o objeto inteiro e re-renderiza todos os consumidores.
  • Mesmo com separação, componentes que consomem o estado ainda re-renderizam quando ele muda. Para estados muito “quentes” (mudam com alta frequência), Context pode virar gargalo.

Dica: estabilize referências com useMemo/useCallback

Se você recria funções/objetos a cada render do Provider, o Context de ações também muda e causa renders. Por isso usamos useCallback nas funções e useMemo para o objeto actions.

Quando Context não é suficiente (sinais de complexidade)

Context API é ótima, mas tem limites. Considere alternativas (ou uma arquitetura mais robusta) quando aparecerem sinais como:

  • Muitos contexts interdependentes com “efeitos em cascata” (ex.: atualizar Auth precisa limpar Cart, atualizar Preferences precisa recalcular várias telas).
  • Atualizações muito frequentes (ex.: tracking em tempo real, digitação sincronizada, streaming de eventos) causando re-renderizações amplas.
  • Estado muito normalizado e complexo (entidades relacionadas, cache de requests, invalidação, paginação, deduplicação).
  • Debug difícil: você precisa rastrear “quem alterou o quê” e sente falta de ferramentas de inspeção/time-travel.
  • Regras de negócio espalhadas: ações começam a chamar outras ações, reducers crescem demais e você cria muitos “helpers” para manter consistência.

Mitigações antes de trocar de abordagem

  • Dividir por domínio (como fizemos) e evitar um Context único.
  • Separar estado e ações e manter ações estáveis.
  • Extrair seletores (funções que calculam derivados) e memorizar com useMemo.
  • Evitar colocar no Context dados que podem ficar locais (ex.: estado de formulário de uma tela).
  • Reduzir o tamanho do estado persistido: persista apenas o necessário (ex.: token, preferências, itens do carrinho), não caches enormes.

Checklist de implementação (passo a passo)

PassoO que fazerResultado
1Definir tipos e estado inicialContrato claro do domínio
2Criar reducer com ações explícitasAtualizações previsíveis
3Criar storage (load/save/clear)Persistência e reidratação
4Provider com useReducer + useEffect (hydrate/persist)Estado global funcional
5Separar Context de estado e de açõesMenos renders em consumidores de ações
6Expor hooks (useXState, useXActions, seletores)Consumo simples e padronizado
7Adicionar gate de isHydrated no rootSem “flash” de estado padrão

Agora responda o exercício sobre o conteúdo:

Em um app React Native que usa Context API com persistência, qual abordagem ajuda a reduzir re-renderizações desnecessárias em componentes que apenas disparam comandos (como sair da conta)?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Ao separar estado e ações, componentes que consomem apenas ações não re-renderizam quando o estado muda. Além disso, estabilizar funções/objetos com useCallback e useMemo evita que o Context de ações mude a cada render do Provider.

Próximo capitúlo

Visão introdutória de Redux Toolkit e Zustand para React Native

Arrow Right Icon
Capa do Ebook gratuito React Native Essencial: criando apps completos com JavaScript e boas práticas
44%

React Native Essencial: criando apps completos com JavaScript e boas práticas

Novo curso

16 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.