vike-react-query
TypeScript icon, indicating that this package has built-in type declarations

0.1.1 • Public • Published

npm version

vike-react-query

Enables your React components to fetch data using TanStack Query.

[!NOTE] You also get progressive rendering, fallback upon loading and/or error, and caching.

Installation
Basic usage
withFallback()
Settings
Usage with Telefunc
How it works
See also


Installation

  1. npm install @tanstack/react-query vike-react-query
  2. Extend +config.js:
    // pages/+config.js
    
    import vikeReact from 'vike-react/config'
    import vikeReactQuery from 'vike-react-query/config'
    
    export default {
      // ...
      extends: [vikeReact, vikeReactQuery]
    }

[!NOTE] The vike-react-query extension requires vike-react.


Basic usage

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

const Movie = ({ id }) => {
  const result = useSuspenseQuery({
    queryKey: ['movie', id],
    queryFn: () =>
      fetch(`https://brillout.github.io/star-wars/api/films/${id}.json`)
      .then((res) => res.json())
  })

  const { title } = result.data

  return (
    <div>
      Title: <b>{title}</b>
    </div>
  )
}

[!NOTE] Even though useSuspenseQuery() is imported from @tanstack/react-query, you still need to install vike-react-query for it to work.


withFallback()

withFallback(Component) // Use default loading fallback (see +Loading)
withFallback(Component, Loading) // Define loading fallback
withFallback(Component, Loading, Error) // Define loading and error fallback
withFallback(Component, undefined, Error) // Define error fallback
// Movie.tsx

import { useSuspenseQuery } from '@tanstack/react-query'
import { withFallback } from 'vike-react-query'

const Movie = withFallback(
  ({ id }: { id: string }) => {
    const result = useSuspenseQuery({
      queryKey: ['movie', id],
      queryFn: () =>
        fetch(`https://brillout.github.io/star-wars/api/films/${id}.json`)
        .then((res) => res.json())
    })

    const { title } = result.data

    return (
      <div>
        Title: <b>{title}</b>
      </div>
    )
  },
  ({ id }) => <div>Loading movie {id}</div>,
  // The props `retry` and `error` are provided by vike-react-query
  // Other props, such as `code`, are provied by the parent component
  ({ id, retry, error }) => (
    <div>
      Failed to load movie {id}
      <button onClick={() => retry()}>Retry</button>
    </div>
  )
)

+Loading

If you skip the Loading parameter, then a default loading component (provided by vike-react) is used. You can create a custom default loading component:

// pages/+Loading.jsx

export default { component: LoadingComponent }

function LoadingComponent() {
  // Applies on a component-level
  return <div>Loading...</div>
}

Instead of adding a loading fallback to the component, you can set a loading fallback to the page and layouts:

// pages/+Loading.jsx

export default { layout: LoadingLayout }

function LoadingLayout() {
  // Applies to the page and all layouts
  return <div>Loading...</div>
}

[!NOTE] The +Loading.layout setting is optional and only relevant when using useSuspenseQuery() without withFallback() or withFallback(Component, false).

withFallback(Component, false) // Don't set any loading fallback
withFallback(Component, undefined) // Use default loading fallback

Manual <Suspense> boundary

Technically speaking:

You can also manually add a <Suspense> boundary at any arbitrary position:

import { Suspense } from 'react'

function SomePageSection() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <SomeDataFetchingComponent />
      <SomeOtherDataFetchingComponent />
    </Suspense>
  )
}

Settings

You can modify the defaults defined by QueryClient.

// +config.js

export default {
  queryClientConfig: {
    defaultOptions: {
      queries: {
        staleTime: 60 * 1000
      }
    }
  }
}

You can apply settings to all pages, a group of pages, or only one page. See Vike Docs > Config > Inheritance.


Usage with Telefunc

You can use vike-react-query with Telefunc.

[!NOTE] By using vike-react-query with Telefunc, you combine RPC with all TanStack Query features.

With Telefunc, the query function always runs on the server.

Query example

// movie.telefunc.ts

export async function getMovie(id: string) {
  const movie = await prisma.movie.findUnique({ where: id })
  return movie;
}
// movie.tsx

import { useSuspenseQuery } from '@tanstack/react-query'
import { withFallback } from 'vike-react-query'
import { getMovie } from './movie.telefunc'

const Movie = withFallback(
  ({ id }: { id: string }) => {
    const query = useSuspenseQuery({
      queryKey: ['movie', id],
      queryFn: () => getMovie(id)
    })

    const { title } = query.data

    return (
      <div>
        Title: <b>{title}</b>
      </div>
    )
  },
  ({ id }) => <div>Loading movie {id}</div>,
  ({ id, retry }) => (
    <div>
      Failed to load movie {id}
      <button onClick={() => retry()}>Retry</button>
    </div>
  )
)

Mutation example

// movie.telefunc.ts

export async function createMovie({ title }: { title: string }) {
  const movie = await prisma.movie.create({ data: { title } })
  return movie
}
// movie.tsx

import { useMutation } from '@tanstack/react-query'
import { createMovie } from './movie.telefunc'

const CreateMovie = () => {
  const ref = useRef<HTMLInputElement>(null)
  const mutation = useMutation({
    mutationFn: createMovie
  })

  const onCreate = () => {
    const title = ref.current?.value || 'No title'
    mutation.mutate({ title })
  }

  return (
    <div>
      <input type="text" ref={ref} />
      <button onClick={onCreate}>Create movie</button>
      {mutation.isPending && 'Creating movie..'}
      {mutation.isSuccess && 'Created movie ' + mutation.data.title}
      {mutation.isError && 'Error while creating the movie'}
    </div>
  )
}

Putting it together

// movie.telefunc.ts

export async function getMovies() {
  const movies = await prisma.movie.findMany()
  return movies;
}
export async function createMovie({ title }: { title: string }) {
  const movie = await prisma.movie.create({ data: { title } })
  return movie
}
// movie.tsx

import { useSuspenseQuery, useMutation } from '@tanstack/react-query'
import { withFallback } from 'vike-react-query'
import { getMovies, createMovie } from './movie.telefunc'

const Movies = withFallback(
  () => {
    const queryClient = useQueryClient()
    const query = useSuspenseQuery({
      queryKey: ['movies'],
      queryFn: () => getMovies()
    })
    const mutation = useMutation({
      mutationFn: createMovie,
      onSuccess() {
        query.invalidateQueries({ queryKey: ['movies'] })
        // or query.refetch()
      }
    })

    const ref = useRef<HTMLInputElement>(null)
    const onCreate = () => {
      const title = ref.current?.value || 'No title'
      mutation.mutate({ title })
    }

    return (
      <div>
        {query.data.map((movie) => (
          <div>Title: {movie.title}</div>
        ))}

        <div>
          <input type="text" ref={ref} />
          <button onClick={onCreate}>Create movie</button>
          {mutation.isPending && 'Creating movie..'}
          {mutation.isSuccess && 'Created movie' + mutation.data.title}
          {mutation.isError && 'Error while creating the movie'}
        </div>
      </div>
    )
  },
  <div>Loading movies</div>,
  ({ retry }) => (
    <div>
      Error while loading movies
      <button onClick={() => retry()}>Retry</button>
    </div>
  )
)

How it works

Upon SSR, the component is rendered to HTML and its data loaded on the server-side. On the client side, the component is merely hydrated.

Upon page navigation (and rendering the first page if SSR is disabled), the component is rendered and its data loaded on the client-side.

[!NOTE] With vike-react-query you fetch data on a component-level instead of using Vike's data() hook which fetches data on a page-level.

[!NOTE] Behind the scenes vike-react-query integrates TanStack Query into the HTML stream.


See also

/vike-react-query/

    Package Sidebar

    Install

    npm i vike-react-query

    Weekly Downloads

    279

    Version

    0.1.1

    License

    MIT

    Unpacked Size

    29.4 kB

    Total Files

    18

    Last publish

    Collaborators

    • brillout
    • nitedani