Docs
CodeRabbit
Cloudflare
AG Grid
SerpAPI
Netlify
OpenRouter
Neon
WorkOS
Clerk
Electric
PowerSync
Sentry
Railway
Prisma
Strapi
Unkey
CodeRabbit
Cloudflare
AG Grid
SerpAPI
Netlify
OpenRouter
Neon
WorkOS
Clerk
Electric
PowerSync
Sentry
Railway
Prisma
Strapi
Unkey
API Reference

React Example: Batching

import ReactDOM from 'react-dom/client'
import {
  QueryClient,
  QueryClientProvider,
  queryOptions,
  useQueries,
} from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { useState } from 'react'
import { loadCharacter } from './character-loader'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 0,
    },
  },
})

const characterQueryOptions = (id: number) =>
  queryOptions({
    queryKey: ['character', id],
    queryFn: () => loadCharacter(id),
    staleTime: 60 * 1000,
  })

function Characters({ ids }: { ids: Array<number> }) {
  const characters = useQueries({
    queries: ids.map((id) => characterQueryOptions(id)),
  })

  return characters.map((character, index) => {
    if (character.status === 'pending') {
      return 'Loading...'
    }
    if (character.status === 'error') {
      return 'Error...'
    }
    if (character.data === null) {
      return 'Not found ...'
    }

    return (
      <div key={String(index)} style={{ display: 'flex' }}>
        <img
          width={100}
          height={100}
          alt={character.data.image}
          src={character.data.image}
        />
        <div style={{ paddingLeft: 10, textAlign: 'left' }}>
          <h5 style={{ marginTop: 0, marginBottom: 5 }}>
            {character.data.name} (#{character.data.id})
          </h5>
          <p style={{ marginTop: 0, marginBottom: 2 }}>
            Status: {character.data.status}
          </p>
          <p style={{ marginTop: 0, marginBottom: 2 }}>
            Species: {character.data.species}
          </p>
          <p style={{ marginTop: 0, marginBottom: 2 }}>
            Gender: {character.data.gender}
          </p>
        </div>
      </div>
    )
  })
}

function App() {
  const [ids, setIds] = useState<Array<number>>([])
  const [idsToLoad, setIdsToLoad] = useState<Array<number>>([])
  const [error, setError] = useState('')

  return (
    <div className="App">
      <h2>Character fetcher</h2>
      <div>
        <label style={{ display: 'block' }}>
          Character ids (numberic, comma seprated)
        </label>
        <input
          style={{ display: 'inline-block' }}
          placeholder="ids"
          onChange={(event) => {
            const text = event.target.value.replace(' ', '')
            const parsedIds = text.split(',').map((id) => parseInt(id, 10))
            const allValid = parsedIds.reduce(
              (valid, id) => valid && !isNaN(id),
              true,
            )

            if (allValid) {
              setIds(parsedIds)
              setError('')
            } else {
              setError('Invalid.. only use numbers and comma')
            }
          }}
        />
      </div>

      {error ? <h3 style={{ color: 'red' }}>{error}</h3> : null}

      <button
        type="button"
        onClick={() => {
          setIdsToLoad(ids)
        }}
      >
        Load characters
      </button>
      <div>
        <h4 style={{ textAlign: 'left' }}>Characters:</h4>
        <Characters ids={idsToLoad} />
      </div>
    </div>
  )
}

function Root() {
  return (
    <QueryClientProvider client={queryClient}>
      <App />
      <ReactQueryDevtools initialIsOpen />
    </QueryClientProvider>
  )
}

const rootElement = document.getElementById('root')
if (!rootElement) throw new Error('Missing #root element')
ReactDOM.createRoot(rootElement).render(<Root />)
import ReactDOM from 'react-dom/client'
import {
  QueryClient,
  QueryClientProvider,
  queryOptions,
  useQueries,
} from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { useState } from 'react'
import { loadCharacter } from './character-loader'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 0,
    },
  },
})

const characterQueryOptions = (id: number) =>
  queryOptions({
    queryKey: ['character', id],
    queryFn: () => loadCharacter(id),
    staleTime: 60 * 1000,
  })

function Characters({ ids }: { ids: Array<number> }) {
  const characters = useQueries({
    queries: ids.map((id) => characterQueryOptions(id)),
  })

  return characters.map((character, index) => {
    if (character.status === 'pending') {
      return 'Loading...'
    }
    if (character.status === 'error') {
      return 'Error...'
    }
    if (character.data === null) {
      return 'Not found ...'
    }

    return (
      <div key={String(index)} style={{ display: 'flex' }}>
        <img
          width={100}
          height={100}
          alt={character.data.image}
          src={character.data.image}
        />
        <div style={{ paddingLeft: 10, textAlign: 'left' }}>
          <h5 style={{ marginTop: 0, marginBottom: 5 }}>
            {character.data.name} (#{character.data.id})
          </h5>
          <p style={{ marginTop: 0, marginBottom: 2 }}>
            Status: {character.data.status}
          </p>
          <p style={{ marginTop: 0, marginBottom: 2 }}>
            Species: {character.data.species}
          </p>
          <p style={{ marginTop: 0, marginBottom: 2 }}>
            Gender: {character.data.gender}
          </p>
        </div>
      </div>
    )
  })
}

function App() {
  const [ids, setIds] = useState<Array<number>>([])
  const [idsToLoad, setIdsToLoad] = useState<Array<number>>([])
  const [error, setError] = useState('')

  return (
    <div className="App">
      <h2>Character fetcher</h2>
      <div>
        <label style={{ display: 'block' }}>
          Character ids (numberic, comma seprated)
        </label>
        <input
          style={{ display: 'inline-block' }}
          placeholder="ids"
          onChange={(event) => {
            const text = event.target.value.replace(' ', '')
            const parsedIds = text.split(',').map((id) => parseInt(id, 10))
            const allValid = parsedIds.reduce(
              (valid, id) => valid && !isNaN(id),
              true,
            )

            if (allValid) {
              setIds(parsedIds)
              setError('')
            } else {
              setError('Invalid.. only use numbers and comma')
            }
          }}
        />
      </div>

      {error ? <h3 style={{ color: 'red' }}>{error}</h3> : null}

      <button
        type="button"
        onClick={() => {
          setIdsToLoad(ids)
        }}
      >
        Load characters
      </button>
      <div>
        <h4 style={{ textAlign: 'left' }}>Characters:</h4>
        <Characters ids={idsToLoad} />
      </div>
    </div>
  )
}

function Root() {
  return (
    <QueryClientProvider client={queryClient}>
      <App />
      <ReactQueryDevtools initialIsOpen />
    </QueryClientProvider>
  )
}

const rootElement = document.getElementById('root')
if (!rootElement) throw new Error('Missing #root element')
ReactDOM.createRoot(rootElement).render(<Root />)