React Query Overview

Jul 11, 2024

React Query Overview

Introduction

  • Presenter: Kyle from Web Dev Simplified
  • Purpose: Introduction and deep dive into TanStack Query (React Query)
  • Promises to show the utility of React Query in simplifying asynchronous state management, specifically for API requests

What is React Query?

  • Asynchronous State Management Library
    • Manages state from API requests
    • Handles caching, pre-fetching, and data synchronization
  • Benefits
    • Simplifies the process of fetching data
    • Automates retries, data syncing, and error handling

Getting Started

  1. Setup

    • Create a React Application (e.g., using Vite)
    • Install React Query and Dev Tools
      npm install @tanstack/react-query
      npm install @tanstack/react-query-devtools
      
  2. Basic Configuration

    • Include QueryClient and QueryClientProvider to wrap your main app component
      import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
      
      const queryClient = new QueryClient();
      
      function App() {
        return (
          <QueryClientProvider client={queryClient}>
            <YourMainComponent />
          </QueryClientProvider>
        );
      }
      

Core Concepts

Queries

  • Fetching data (e.g., list of posts)
  • Use useQuery hook
    import { useQuery } from '@tanstack/react-query';
    
    const fetchPosts = async () => {
      const response = await fetch('/api/posts');
      return response.json();
    };
    
    function Posts() {
      const { data, error, isLoading } = useQuery(['posts'], fetchPosts);
    
      if (isLoading) return 'Loading...';
      if (error) return 'Error!';
    
      return (
        <div>
          {data.map(post => (
            <div key={post.id}>{post.title}</div>
          ))}
        </div>
      );
    }
    
  • Important Properties: data, error, isLoading, status, etc.

Mutations

  • Changing data (e.g., creating a new post)
  • Use useMutation hook
    import { useMutation, useQueryClient } from '@tanstack/react-query';
    
    const createPost = async (newPost) => {
      const response = await fetch('/api/posts', {
        method: 'POST',
        body: JSON.stringify(newPost),
      });
      return response.json();
    };
    
    function CreatePost() {
      const queryClient = useQueryClient();
      const mutation = useMutation(createPost, {
        onSuccess: () => {
          queryClient.invalidateQueries(['posts']);
        }
      });
    
      return (
        <button onClick={() => mutation.mutate({ title: 'New Post' })}>
          Create Post
        </button>
      );
    }
    
  • Important Properties: data, error, isLoading, mutate, mutateAsync, etc.

Query Keys and Caching

  • Key Uniqueness: Compound keys for unique identifiers (e.g., ['posts', postId])
  • Cache Management: Use invalidateQueries to update cache after mutations
    queryClient.invalidateQueries(['posts', postId])
    

Handling Query States

  • Loading: Use isLoading to show loading state
  • Error: Handle errors with isError and error object
  • Success: Automatically managed, no special handling usually required

Dev Tools

  • Visualize queries and mutations
    import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
    
    <ReactQueryDevtools initialIsOpen={false} />
    

Advanced Features

Paginated and Infinite Queries

  • Pagination: Managing page number in query keys
    const fetchPosts = async ({ pageParam = 1 }) => {
      const response = await fetch(`/api/posts?page=${pageParam}`);
      return response.json();
    };
    
    const { data, isLoading, fetchNextPage } = usePaginatedQuery('posts', fetchPosts);
    
  • Infinite Scroll: Using useInfiniteQuery for loading more data
    import { useInfiniteQuery } from '@tanstack/react-query';
    
    const fetchPosts = ({ pageParam = 1 }) => fetch(`/api/posts?page=${pageParam}`).then(res => res.json());
    
    const { data, fetchNextPage, hasNextPage } = useInfiniteQuery('posts', fetchPosts, {
      getNextPageParam: lastPage => lastPage.nextPage ?? false
    });
    

Prefetching

  • Prefetch Query: Preloading data before navigation
    queryClient.prefetchQuery(['post', id], () => fetchPost(id));
    

Placeholder and Initial Data

  • Placeholder Data: Temporary display data while loading
  • Initial Data: Assumed valid for caching
    useQuery('posts', fetchPosts, {
      placeholderData: [{ id: 0, title: 'Loading...' }],
    });
    

Conclusion

  • React Query greatly simplifies asynchronous state management
  • Flexibility: From simple data fetching to complex state management tasks
  • Optimization: With caching, prefetching, pagination, and more

Interested? Check out Kyle's full React course for a deeper dive.