useQuery

React hook to fetch, cache, and revalidate asynchronous data with automatic key tracking and lifecycle callbacks.

pnpm add react-callback-hooks

Demo

Loading...
Switching back to a cached pokemon loads instantly.

Usage

Shorthand — key + fetcher + optional callbacks:

import { useQuery } from 'react-callback-hooks'

const [{ data, loading, error }, refetch] = useQuery(
  'users',
  () => fetch('/api/users').then((r) => r.json()),
  {
    onSuccess: (data) => console.log(data),
    onError: (err) => console.error(err)
  }
)

Compound key — re-fetches whenever any part changes:

const [{ data }] = useQuery(['user', userId], () =>
  fetch(`/api/users/${userId}`).then((r) => r.json())
)

Object form — full control:

const [{ data, loading, error }, refetch] = useQuery({
  key: ['user', userId],
  queryFn: () => fetch(`/api/users/${userId}`).then((r) => r.json()),
  staleTime: 30_000,
  enabled: !!userId,
  onSuccess: (data) => console.log(data),
  onError: (err) => console.error(err)
})

Parameters

Shorthand form

ParameterTypeDescription
keystring | string[]Cache key. Array keys are joined and re-fetched when any part changes.
queryFn() => Promise<T>Async function that resolves the data.
options.onSuccess(data: T) => voidCalled when the fetch resolves successfully.
options.onError(error: Error) => voidCalled when the fetch rejects.

Object form

PropertyTypeDefaultDescription
keystring | string[]Cache key.
queryFn() => Promise<T>Async function that resolves the data.
staleTimenumberundefinedMs before a cached entry is re-fetched.
enabledbooleantrueSet to false to skip fetching until ready.
onSuccess(data: T) => voidCalled on successful fetch.
onError(error: Error) => voidCalled on fetch error.

Return Values

Returns a tuple [state, refetch]:

TypeDescription
state.dataT | nullResolved data, null while loading or on error.
state.loadingbooleantrue while a fetch is in flight.
state.errorError | nullLast error, or null if the last fetch succeeded.
refetch() => voidForces a new fetch, bypassing the cache.