00
:
00
:
00
:
00
Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Prefetching

Perche’ Fare Prefetching

Il prefetching consente di caricare i dati prima che l’utente ne abbia bisogno. Quando poi naviga verso una pagina, i dati sono gia’ in cache e la UI appare istantaneamente.

queryClient.prefetchQuery

Il metodo piu’ diretto per precaricare dati:

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

function PostList({ posts }: { posts: Post[] }) {
  const queryClient = useQueryClient()

  return (
    <ul>
      {posts.map((post) => (
        <li
          key={post.id}
          onMouseEnter={() => {
            // Precarica il dettaglio quando l'utente passa sopra col mouse
            queryClient.prefetchQuery({
              queryKey: ['post', post.id],
              queryFn: () => fetchPost(post.id),
              staleTime: 1000 * 60 * 5, // considera fresco per 5 minuti
            })
          }}
        >
          <Link to={`/posts/${post.id}`}>{post.title}</Link>
        </li>
      ))}
    </ul>
  )
}

Prefetch su Hover

Un pattern molto comune e’ precaricare quando l’utente passa il mouse su un link:

function NavLink({ to, queryKey, queryFn, children }: NavLinkProps) {
  const queryClient = useQueryClient()

  const handleMouseEnter = () => {
    queryClient.prefetchQuery({
      queryKey,
      queryFn,
      staleTime: 1000 * 60, // 1 minuto
    })
  }

  return (
    <Link to={to} onMouseEnter={handleMouseEnter}>
      {children}
    </Link>
  )
}

// Uso
<NavLink
  to="/dashboard"
  queryKey={['dashboard', 'stats']}
  queryFn={fetchDashboardStats}
>
  Dashboard
</NavLink>

Prefetch su Route Change

Con React Router puoi precaricare dati durante la navigazione usando i loader:

import { QueryClient } from '@tanstack/react-query'
import { createBrowserRouter } from 'react-router-dom'

const queryClient = new QueryClient()

const router = createBrowserRouter([
  {
    path: '/posts/:postId',
    loader: async ({ params }) => {
      // Precarica (o usa dalla cache) il post
      await queryClient.ensureQueryData({
        queryKey: ['post', Number(params.postId)],
        queryFn: () => fetchPost(Number(params.postId)),
        staleTime: 1000 * 60,
      })
      return null
    },
    element: <PostDetail />,
  },
])

La differenza tra prefetchQuery e ensureQueryData:

  • prefetchQuery: Lancia il fetch ma non aspetta il risultato. Non lancia errori.
  • ensureQueryData: Aspetta il risultato e lo ritorna. Utile nei loader.

initialData vs placeholderData

Sono due strategie diverse per mostrare dati prima che il fetch completi:

initialData

Dati “reali” che vengono messi in cache. Utili quando hai gia’ i dati da un’altra query:

function PostDetail({ postId }: { postId: number }) {
  const { data } = useQuery({
    queryKey: ['post', postId],
    queryFn: () => fetchPost(postId),
    initialData: () => {
      // Cerca il post nella cache della lista
      const posts = queryClient.getQueryData<Post[]>(['posts'])
      return posts?.find((p) => p.id === postId)
    },
    // Quando i dati iniziali sono stati messi in cache
    initialDataUpdatedAt: () => {
      return queryClient.getQueryState(['posts'])?.dataUpdatedAt
    },
  })

  return <h1>{data?.title}</h1>
}

placeholderData

Dati “finti” che non vengono messi in cache. Mostrati solo durante il loading:

function PostDetail({ postId }: { postId: number }) {
  const { data, isPlaceholderData } = useQuery({
    queryKey: ['post', postId],
    queryFn: () => fetchPost(postId),
    placeholderData: (previousData) => {
      // Mostra i dati del post precedente mentre carica il nuovo
      return previousData
    },
  })

  return (
    <div style={{ opacity: isPlaceholderData ? 0.5 : 1 }}>
      <h1>{data?.title}</h1>
    </div>
  )
}

Confronto

Caratteristica initialData placeholderData
Viene messa in cache Si No
Influenza staleTime Si No
isPlaceholderData Sempre false true durante il caricamento
Uso tipico Dati da un’altra query Dati finti o precedenti

Hydration per SSR

Per il Server-Side Rendering, puoi precaricare i dati sul server e passarli al client tramite dehydrate/hydrate:

// server.tsx (o getServerSideProps in Next.js Pages Router)
import { dehydrate, QueryClient } from '@tanstack/react-query'

export async function getServerSideProps() {
  const queryClient = new QueryClient()

  await queryClient.prefetchQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts,
  })

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  }
}
// _app.tsx
import { HydrationBoundary } from '@tanstack/react-query'

function MyApp({ Component, pageProps }: AppProps) {
  const [queryClient] = useState(() => new QueryClient())

  return (
    <QueryClientProvider client={queryClient}>
      <HydrationBoundary state={pageProps.dehydratedState}>
        <Component {...pageProps} />
      </HydrationBoundary>
    </QueryClientProvider>
  )
}

Prefetching di Infinite Queries

Puoi anche precaricare query infinite:

await queryClient.prefetchInfiniteQuery({
  queryKey: ['posts', 'infinite'],
  queryFn: ({ pageParam }) => fetchPostsPage(pageParam),
  initialPageParam: 0,
  pages: 3, // precarica le prime 3 pagine
  getNextPageParam: (lastPage) => lastPage.nextCursor,
})