0Pricing
React Native Academy · บทเรียน

QueryClient, QueryClientProvider และ useQuery

ตั้งค่า TanStack Query ด้วย QueryClient ครอบแอปด้วย QueryClientProvider และดึงข้อมูลด้วย useQuery แทนการดึงข้อมูลด้วย useEffect แบบที่ทำเอง

QueryClient, QueryClientProvider และ useQuery เป็นบทเรียน React Native Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน React Native Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส React Native Academy มีบทเรียนทั้งหมด 4 บทเรียน

บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ

What Is TanStack React Query?

TanStack React Query (formerly React Query) is a data-fetching and caching library that manages server state in your React Native app. Instead of managing loading, error, and data state manually with useState and useEffect, React Query provides smart caching, background refetching, and automatic retry — all with a simple hook API.

Server state is inherently different from UI state: it lives on a remote server, can be stale, needs refetching, and multiple components might need the same data. React Query solves all of this in a battle-tested, production-proven way.

// Traditional approach (before React Query):
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

// With React Query:
const { data, isLoading, error } = useQuery({
  queryKey: ['posts'],
  queryFn: fetchPosts,
});
// Same result — dramatically less code

Installing TanStack Query

Install TanStack Query (v5) and the React Native specific persistence plugin. The core package is @tanstack/react-query. For Expo projects you may also want the devtools, though they require a web environment to view.

React Query v5 uses object syntax for hook options (breaking change from v4). If you see tutorials using positional arguments for useQuery, they are using v4. This lesson covers the modern v5 API.

// Install:
// npx expo install @tanstack/react-query
// npx expo install @tanstack/react-query-persist-client
// npx expo install @tanstack/async-storage-persister
// npx expo install @react-native-async-storage/async-storage

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

Setting Up QueryClient

QueryClient is the core object that manages all caches, queries, and mutations. You create one instance and share it across the entire app. Configure global defaults in the QueryClient constructor — these apply to all queries unless overridden at the individual query level.

Create the QueryClient outside your component tree (or in a useState initializer) so it is not recreated on every render. A recreated QueryClient loses all cached data.

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

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000,  // 5 minutes before considered stale
      gcTime: 10 * 60 * 1000,    // 10 minutes before garbage collected
      retry: 2,                   // retry failed requests twice
      refetchOnWindowFocus: false, // disable for mobile
    },
  },
});

QueryClientProvider: Making the Client Available

Wrap your app's root in QueryClientProvider, passing the QueryClient instance as the client prop. Every component inside the provider can then access the QueryClient and use the React Query hooks. This is similar to React Redux's Provider or React Navigation's NavigationContainer.

Place QueryClientProvider at the root of your app, wrapping navigation and other providers. The order with other providers (like Redux Provider or AuthProvider) typically doesn't matter unless they depend on each other.

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient({
  defaultOptions: { queries: { staleTime: 5 * 60 * 1000 } },
});

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <NavigationContainer>
        <RootNavigator />
      </NavigationContainer>
    </QueryClientProvider>
  );
}

useQuery: Fetching Data

useQuery is the primary hook for fetching and caching server data. It takes a config object with two required fields: queryKey (a unique identifier for this data) and queryFn (the async function that fetches the data). React Query calls queryFn automatically and caches the result.

The hook returns data, isLoading, isFetching, error, and many other state values. isLoading is true on the first load; isFetching is true any time a fetch is happening (including background refetches).

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

async function fetchPosts() {
  const response = await fetch('https://api.example.com/posts');
  if (!response.ok) throw new Error('Network error');
  return response.json();
}

function PostsScreen() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts,
  });

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

Query Keys: The Cache Identity

The queryKey is how React Query identifies and reuses cached data. Use an array that includes the data type and any variables the fetch depends on. Queries with the same key share cached data — multiple components using ['posts'] only fetch once and share the same data object.

Include dynamic parameters in the key array so different parameter combinations get separate cache entries. For example, ['user', userId] caches each user's data separately. Keys are compared by value (deep equality), not by reference.

// Static key:
const { data: posts } = useQuery({
  queryKey: ['posts'],
  queryFn: fetchAllPosts,
});

// Dynamic key — separate cache per user:
const { data: user } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
});

// Compound key with filters:
const { data: filteredPosts } = useQuery({
  queryKey: ['posts', { status: 'published', category: 'tech' }],
  queryFn: () => fetchPosts({ status: 'published', category: 'tech' }),
});

staleTime and cacheTime

staleTime determines how long cached data is considered fresh before React Query fetches fresh data in the background. During this time, the cached value is returned immediately without any network request. After staleTime the data is considered stale and a background refetch is triggered on the next mount or focus.

gcTime (formerly cacheTime) determines how long unused (no observers) cache entries are kept in memory before being garbage collected. The cache exists even after all components using it unmount, until gcTime expires.

const { data } = useQuery({
  queryKey: ['settings'],
  queryFn: fetchUserSettings,
  staleTime: 10 * 60 * 1000,  // fresh for 10 min, no background refetch
  gcTime: 30 * 60 * 1000,     // kept in cache for 30 min after unmount
});

// Infinity means never stale (manual invalidation only):
const { data: config } = useQuery({
  queryKey: ['app-config'],
  queryFn: fetchAppConfig,
  staleTime: Infinity,
});

Dependent Queries

Some queries depend on data from another query. Use the enabled option to disable a query until its dependency is ready. React Query won't call the queryFn when enabled is false, and it will start fetching automatically once enabled becomes true.

This replaces nested useEffect chains where you'd wait for one fetch to complete before starting another. The dependent query pattern is declarative and easier to reason about.

// Fetch user first:
const { data: user } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
});

// Then fetch user's posts when user is available:
const { data: userPosts } = useQuery({
  queryKey: ['posts', user?.id],
  queryFn: () => fetchUserPosts(user.id),
  enabled: !!user, // only runs after user is fetched
});

Retry Behavior and Error Handling

React Query automatically retries failed queries with exponential backoff. The default is 3 retries. Configure retry (number) and retryDelay (ms or function) per query. After all retries fail, the error field is populated and isError becomes true.

The queryFn must throw an error to signal failure — returning null or a special error object won't trigger retry. Always throw in the query function when the response indicates an error (check HTTP status codes explicitly).

const { data, isError, error } = useQuery({
  queryKey: ['data'],
  queryFn: async () => {
    const res = await fetch('https://api.example.com/data');
    if (!res.ok) {
      throw new Error('Request failed: ' + res.status); // must throw!
    }
    return res.json();
  },
  retry: 3,
  retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30000),
});

Manual Query Invalidation

When you know data has changed on the server (after a mutation, for example), invalidate the relevant queries using queryClient.invalidateQueries({ queryKey: ['posts'] }). This marks the cached data as stale and triggers a background refetch for any components currently observing the query.

Query invalidation is one of the most powerful patterns in React Query — after posting a new comment, invalidate the comments query and the list refreshes automatically. No manual state update needed.

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

function PostButton() {
  const queryClient = useQueryClient();

  async function handlePost() {
    await submitPost(newPostData);
    // Invalidate posts list — triggers background refetch:
    await queryClient.invalidateQueries({ queryKey: ['posts'] });
  }
}

Background Refetch on App Focus

React Query can automatically refetch stale data when the user returns to the app from the background. On web this uses window focus; on React Native you need to configure it using the focusManager with AppState. This keeps data fresh automatically without the user manually refreshing.

Setup the focus manager once at app startup. After the setup, any stale query will automatically refetch when the user switches back to the app, keeping the UI current without any additional code in individual components.

import { focusManager } from '@tanstack/react-query';
import { AppState } from 'react-native';

// At app startup (App.js or index.js):
AppState.addEventListener('change', (state) => {
  focusManager.setFocused(state === 'active');
});

// Queries with staleTime < Infinity will refetch
// automatically when app comes to foreground

Quick Check

Test your understanding of React Native Mobile Development concepts from this lesson.

Lesson Recap

In this lesson you learned: QueryClient holds the cache and is provided to the component tree via QueryClientProvider, useQuery fetches and caches server data identified by queryKey and calls queryFn automatically, and staleTime controls how long cached data is considered fresh before a background refetch is triggered. Next up we use useMutation to modify server data and automatically refresh related queries.

คำถามที่พบบ่อย

บทเรียน “QueryClient, QueryClientProvider และ useQuery” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “QueryClient, QueryClientProvider และ useQuery” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส React Native Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส React Native Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “QueryClient, QueryClientProvider และ useQuery”

ตั้งค่า TanStack Query ด้วย QueryClient ครอบแอปด้วย QueryClientProvider และดึงข้อมูลด้วย useQuery แทนการดึงข้อมูลด้วย useEffect แบบที่ทำเอง คุณปฏิบัติ React Native Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน React Native Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน React Native Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “QueryClient, QueryClientProvider และ useQuery” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน React Native Academy นี้ได้ไหม

ได้ บทเรียน React Native Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. QueryClient, QueryClientProvider และ useQuery
  2. การเปลี่ยนแปลงด้วย useMutation และการทำให้แคชไม่ถูกต้อง
  3. การคงแคชคำค้นด้วย AsyncStorage
  4. การดึงข้อมูลเบื้องหลังและการกำหนดเวลาข้อมูลเก่า
← กลับไปที่ React Native Academy