Launch-Native Logo
State & Data Fetching

TanStack Query (Data)

Fetch, cache, and update async data from Supabase.

Launch-Native uses TanStack Query v5 (formerly React Query) to handle all asynchronous data. It is the industry standard for fetching data, caching it, and handling loading/error states.

1. Why TanStack Query?

When you fetch data directly with useEffect, you have to manually handle loading states, errors, and caching. TanStack Query does all of this automatically.

Combined with Supabase, it provides a seamless experience:

  • It caches your database queries so your app feels instantly fast.
  • It automatically refetches data when the user focuses the app.
  • It provides a simple API for mutations (creating/updating data).

2. Fetching Data (useQuery)

Here is how you fetch the user's profile from Supabase using our custom hook pattern:

import { useQuery } from "@tanstack/react-query";
import { supabase } from "@/lib/supabase";
import { Text, ActivityIndicator } from "react-native";

export function UserProfile({ userId }: { userId: string }) {
  const { data, isLoading, error } = useQuery({
    queryKey: ["profile", userId], // Unique key for caching
    queryFn: async () => {
      const { data, error } = await supabase
        .from("profiles")
        .select("*")
        .eq("id", userId)
        .single();

      if (error) throw new Error(error.message);
      return data;
    },
  });

  if (isLoading) return <ActivityIndicator />;
  if (error) return <Text>Error: {error.message}</Text>;

  return <Text>Hello, {data.full_name}!</Text>;
}

3. Updating Data (useMutation)

When you need to update the database (like updating a username), you use useMutation.

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

export function updateUsername() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: async (newName: string) => {
      const { data, error } = await supabase
        .from("profiles")
        .update({ full_name: newName })
        .eq("id", "USER_ID");

      if (error) throw error;
      return data;
    },
    onSuccess: () => {
      // Invalidate the cache to force a refetch!
      queryClient.invalidateQueries({ queryKey: ["profile"] });
    },
  });

  return mutation;
}

By keeping your data fetching logic wrapped in TanStack Query, your React Native app will remain blazing fast and offline-resilient.