Skip to content

Latest commit

 

History

History

vike-react-apollo

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

npm version

vike-react-apollo

Enables your React components to fetch data using Apollo GraphQL.

Note

You also get progressive rendering, fallback upon loading and/or error, and caching.

Installation
Basic usage
withFallback()
<head> tags
Error Handling
How it works
See also


Installation

  1. npm install @apollo/client @apollo/client-react-streaming graphql vike-react-apollo
  2. Extend +config.js:
    // pages/+config.js
    
    import vikeReact from 'vike-react/config'
    import vikeReactApollo from 'vike-react-apollo/config'
    
    export default {
      // ...
      extends: [vikeReact, vikeReactApollo]
    }
  3. Create +ApolloClient.js:
    // +ApolloClient.js
    
    import { ApolloClient, InMemoryCache } from '@apollo/client-react-streaming'
    
    export default (pageContext: PageContext) =>
       new ApolloClient({
         uri: 'https://countries.trevorblades.com',
         cache: new InMemoryCache()
       })

Note

The vike-react-apollo extension requires vike-react.


Basic usage

// Countries.jsx

import { useSuspenseQuery, gql } from '@apollo/client/index.js'

const Countries = () => {
  const { data } = useSuspenseQuery(gql`
    {
      countries {
        code
        name
      }
    }
  `)

  return (
    <ul>
      {data.countries.map((country) => (
        <li key={country.code}>{country.name}</li>
      ))}
    </ul>
  )
}

Note

Even though useSuspenseQuery() is imported from @apollo/client, you need to install vike-react-apollo for it to work. (The useSuspenseQuery() hook requires an HTML stream integration.)


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
// Country.jsx

import { useSuspenseQuery, gql } from '@apollo/client/index.js'
import { withFallback } from 'vike-react-apollo'

const Country = withFallback(
  ({ code }) => {
    const { data } = useSuspenseQuery(
      gql`
        query Country($code: String!) {
          country(code: $code) {
            name
          }
        }
      `,
      {
        variables: {
          code
        }
      }
    )

    return (
      <div>
        Name: <b>{data.country.name}</b>
      </div>
    )
  },
  ({ code }) => <div>Loading country {code}</div>,
  // The props `retry` and `error` are provided by vike-react-apollo
  // Other props, such as `code`, are provied by the parent component
  ({ code, retry, error }) => (
    <div>
      Failed to load country {code}
      <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>
  )
}

<head> tags

To set tags such as <title> and <meta name="description"> based on fetched data, you can use <Config>, <Head>, and useConfig().

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

function Movies() {
  const query = useSuspenseQuery(gql`
    {
      movies {
        title
      }
    }
  `)
  const movies = query.data
  return (
    <Config title={`${movies.length} Star Wars Movies`} />
    <Head>
      <meta name="description" content={`All ${movies.length} movies from the Star Wars franchise.`} />
    </Head>
    <ul>{
      movies.map(({ title }) => (
        <li>{title}</li>
      ))
    }</ul>
  )
}

Error Handling

From a UI perspective, the classic approach to handling errors is the following.

  • Show a 404 page, for example <h1>404 Page Not Found</h1><p>This page could not found.</p>.
  • Show an error page, for example <h1>500 Internal Server Error</h1><p>Something went wrong.</p>.
  • Redirect the user, for example redirecting the user to /publish-movie upon /movie/some-fake-movie-title because there isn't any movie some-fake-movie-title.

But because vike-react-apollo leverages HTML streaming these approaches don't work (well) and we recommend the following instead.

  • Show a not-found component, for example <p>No movie <code>some-fake-movie-title</code> found.</p>.
  • Show an error component, for example <p>Something went wrong (couldn't fetch movie), please try again later.</p>.
  • Show a link (instead of redirecting the user), for example <p>No movie <code>some-fake-movie-title</code> found. You can <a href="/publish-movie">publish a new movie</a>.</p>.

See: withFallback()

Note

HTML chunks that are already streamed to the user cannot be reverted and that's why page-level redirection (throw redirect) and rewrite (throw render()) don't work (well).

Also it isn't idiomatic: the whole idea of collocating data-fetching with the UI component is to think in terms of the component in isolation rather than in terms of the page.


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-apollo 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-apollo integrates Apollo GraphQL into the HTML stream.


See also