Mastering Data Fetching with React Query

React Query is a powerful library for managing server state in React applications. It simplifies data fetching, caching, synchronization, and more, making it an essential tool for modern web development. In this article, we'll explore the key features of React Query and provide code snippets to help you get started.

Why Use React Query?

Managing server state in React can be challenging. React Query abstracts the complexity and provides a set of tools to handle data fetching and caching efficiently.

  • Automatic Caching: React Query caches your data and updates it only when necessary.
  • Background Synchronization: Keeps your data up-to-date in the background.
  • Out-of-the-box Support for Pagination and Infinite Queries

Installation

To get started with React Query, install the library using npm or yarn:

npm install @tanstack/react-query

Basic Usage

React Query provides a simple and intuitive API for fetching data. Here's an example of how to fetch data from an API:

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

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data')
  const data = await response.json()
  return data
}

const MyComponent = () => {
  const { data, isLoading, error } = useQuery('data', fetchData)

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return <div>Data: {data}</div>
}

In this example, we define a fetchData function that fetches data from an API. We then use the useQuery hook to fetch the data and handle the loading state, error, and data.

Query Keys

React Query uses a concept called "query keys" to uniquely identify each query. A query key is a string that uniquely identifies a query. It's used to cache the query result and to determine if the query needs to be re-fetched.

By default, React Query uses the query key as the cache key. However, you can also provide a custom cache key by passing a function as the second argument to useQuery:

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data')
  const data = await response.json()
  return data
}

const MyComponent = () => {
  const { data, isLoading, error } = useQuery('data', fetchData, {
    cacheKey: () => 'my-custom-cache-key',
  })

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return <div>Data: {data}</div>
}

Query Functions

React Query provides a set of query functions that simplify common use cases. Here are some examples:

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

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data')
  const data = await response.json()
  return data
}

const MyComponent = () => {
  const { data, isLoading, error } = useQuery('data', fetchData)

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return <div>Data: {data}</div>
}

Query Options

React Query provides a set of options that allow you to customize the behavior of your queries. Here are some examples:

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

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data')
  const data = await response.json()
  return data
}

const MyComponent = () => {
  const { data, isLoading, error } = useQuery('data', fetchData, {
    enabled: false, // Disable the query
    refetchOnWindowFocus: false, // Disable automatic refetching on window focus
    refetchOnMount: false, // Disable automatic refetching on mount
    refetchOnReconnect: false, // Disable automatic refetching on reconnect
    refetchInterval: false, // Disable automatic refetching on interval
    refetchIntervalInBackground: false, // Disable automatic refetching in the background
    refetchIntervalTime: false, // Disable automatic refetching on interval time
    staleTime: false, // Disable automatic refetching on stale time
    cacheTime: false, // Disable automatic refetching on cache time
    cacheKey: false, // Disable automatic refetching on cache key
  })

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return <div>Data: {data}</div>
}

Query Observers

React Query provides a set of observers that allow you to perform side effects when a query changes. Here are some examples:

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

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data')
  const data = await response.json()
  return data
}

const MyComponent = () => {
  const { data, isLoading, error } = useQuery('data', fetchData)

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return (
    <div>
      Data: {data}
      <button onClick={() => refetch()}>Refetch</button>
    </div>
  )
}

Query Aborting

React Query provides a set of aborting functions that allow you to abort a query. Here are some examples:

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

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data')
  const data = await response.json()
  return data
}

const MyComponent = () => {
  const { data, isLoading, error, refetch, cancel } = useQuery(
    'data',
    fetchData,
  )

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return (
    <div>
      Data: {data}
      <button onClick={() => refetch()}>Refetch</button>
      <button onClick={() => cancel()}>Cancel</button>
    </div>
  )
}

Query Mutations

React Query provides a set of mutation functions that allow you to perform mutations on the server. Here are some examples:

import { useMutation } from '@tanstack/react-query'

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data')
  const data = await response.json()
  return data
}

const MyComponent = () => {
  const { mutate, isLoading, error } = useMutation('data', fetchData)

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return (
    <div>
      <button onClick={() => mutate()}>Mutate</button>
    </div>
  )
}

Query Prefetching

React Query provides a set of prefetching functions that allow you to prefetch data before it's needed. Here are some examples:

import { usePrefetch } from '@tanstack/react-query'

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data')
  const data = await response.json()
  return data
}

const MyComponent = () => {
  const { data, isLoading, error } = usePrefetch('data', fetchData)

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return <div>Data: {data}</div>
}

Query Suspense

React Query provides a set of suspense functions that allow you to handle suspense in your components. Here are some examples:

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

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data')
  const data = await response.json()
  return data
}

const MyComponent = () => {
  const { data, isLoading, error } = useQuery('data', fetchData)

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return <div>Data: {data}</div>
}

Query Cache

React Query provides a set of cache functions that allow you to manage the cache of your queries. Here are some examples:

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

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data')
  const data = await response.json()
  return data
}

const MyComponent = () => {
  const { data, isLoading, error } = useQuery('data', fetchData)

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return <div>Data: {data}</div>
}

Query Cache Invalidation

React Query provides a set of cache invalidation functions that allow you to invalidate the cache of your queries. Here are some examples:

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

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data')
  const data = await response.json()
  return data
}

const MyComponent = () => {
  const { data, isLoading, error } = useQuery('data', fetchData)

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return <div>Data: {data}</div>
}

Conclusion

React Query is a powerful library for managing server state in React applications. It simplifies data fetching, caching, synchronization, and more, making it an essential tool for modern web development. In this article, we've explored the key features of React Query and provided code snippets to help you get started.

Resources