State & Data Fetching

Two kinds of state, two tools:

  • Server state (anything that lives on your backend) → @tanstack/react-query.
  • Local / UI state (a toggle, a form step, a selected tab) → React hooks + Context.

No global store (Redux/Zustand) by default. react-query already handles caching, loading, retries, and background refetching for server data — which is most of what a “global store” used to hold. Reach for a global store only when you have genuinely global client state that many screens mutate, and add it deliberately.

Query keys

Keys are arrays, ordered most-general → most-specific. This makes invalidation predictable:

["orders"]            // the list
["orders", orderId]   // one order

A query hook

Wrap each request in a hook that lives in the feature’s hooks/:

// src/features/orders/hooks/useOrders.ts
import { useQuery } from "@tanstack/react-query";
import { api } from "@shared/services/api";

export function useOrders() {
  return useQuery({
    queryKey: ["orders"],
    queryFn: async () => (await api.get("/orders")).data,
    staleTime: 30_000,          // treat data as fresh for 30s
  });
}

Mutations + invalidation

import { useMutation, useQueryClient } from "@tanstack/react-query";

export function useCreateOrder() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (body: NewOrder) => api.post("/orders", body),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["orders"] }),
  });
}

The QueryClientProvider is mounted once at the root, in app/_layout.tsx, so every screen shares one cache.