Skip to content

Commit

Permalink
[Examples] Move with-graphql-hooks to SSG (vercel#13858)
Browse files Browse the repository at this point in the history
Related to vercel#11014

The setup is very similar to the one used for Apollo examples.

@Joezo I've removed the demo example that you added to the readme (vercel#6482) as it will be outdated after this PR is merged.
  • Loading branch information
lfades authored and rokinsky committed Jul 11, 2020
1 parent e15f76a commit a00e85d
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 149 deletions.
Expand Up @@ -30,7 +30,7 @@ export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient()

// If your page has Next.js data fetching methods that use Apollo Client, the initial state
// get hydrated here
// gets hydrated here
if (initialState) {
_apolloClient.cache.restore(initialState)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/with-apollo/lib/apolloClient.js
Expand Up @@ -20,7 +20,7 @@ export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient()

// If your page has Next.js data fetching methods that use Apollo Client, the initial state
// get hydrated here
// gets hydrated here
if (initialState) {
_apolloClient.cache.restore(initialState)
}
Expand Down
2 changes: 0 additions & 2 deletions examples/with-graphql-hooks/README.md
Expand Up @@ -12,8 +12,6 @@ Deploy the example using [Vercel](https://vercel.com):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/with-graphql-hooks)

_Live Example: https://next-with-graphql-hooks.now.sh_

## How to use

### Using `create-next-app`
Expand Down
52 changes: 27 additions & 25 deletions examples/with-graphql-hooks/components/header.js
@@ -1,28 +1,30 @@
import Link from 'next/link'
import { withRouter } from 'next/router'
import { useRouter } from 'next/router'

const Header = ({ router: { pathname } }) => (
<header>
<Link href="/">
<a className={pathname === '/' ? 'is-active' : ''}>Home</a>
</Link>
<Link href="/about">
<a className={pathname === '/about' ? 'is-active' : ''}>About</a>
</Link>
<style jsx>{`
header {
margin-bottom: 25px;
}
a {
font-size: 14px;
margin-right: 15px;
text-decoration: none;
}
.is-active {
text-decoration: underline;
}
`}</style>
</header>
)
export default function Header() {
const { pathname } = useRouter()

export default withRouter(Header)
return (
<header>
<Link href="/">
<a className={pathname === '/' ? 'is-active' : ''}>Home</a>
</Link>
<Link href="/about">
<a className={pathname === '/about' ? 'is-active' : ''}>About</a>
</Link>
<style jsx>{`
header {
margin-bottom: 25px;
}
a {
font-size: 14px;
margin-right: 15px;
text-decoration: none;
}
.is-active {
text-decoration: underline;
}
`}</style>
</header>
)
}
29 changes: 17 additions & 12 deletions examples/with-graphql-hooks/components/post-list.js
@@ -1,4 +1,4 @@
import React, { Fragment, useState } from 'react'
import { useState } from 'react'
import { useQuery } from 'graphql-hooks'
import ErrorMessage from './error-message'
import PostUpvoter from './post-upvoter'
Expand All @@ -19,17 +19,22 @@ export const allPostsQuery = `
}
`

export const allPostsQueryOptions = (skip = 0) => ({
variables: { skip, first: 10 },
updateData: (prevResult, result) => ({
...result,
allPosts: prevResult
? [...prevResult.allPosts, ...result.allPosts]
: result.allPosts,
}),
})

export default function PostList() {
const [skip, setSkip] = useState(0)
const { loading, error, data, refetch } = useQuery(allPostsQuery, {
variables: { skip, first: 10 },
updateData: (prevResult, result) => ({
...result,
allPosts: prevResult
? [...prevResult.allPosts, ...result.allPosts]
: result.allPosts,
}),
})
const { loading, error, data, refetch } = useQuery(
allPostsQuery,
allPostsQueryOptions(skip)
)

if (error) return <ErrorMessage message="Error loading posts." />
if (!data) return <div>Loading</div>
Expand All @@ -38,7 +43,7 @@ export default function PostList() {

const areMorePosts = allPosts.length < _allPostsMeta.count
return (
<Fragment>
<>
<Submit
onSubmission={() => {
refetch({ variables: { skip: 0, first: allPosts.length } })
Expand Down Expand Up @@ -109,6 +114,6 @@ export default function PostList() {
}
`}</style>
</section>
</Fragment>
</>
)
}
40 changes: 40 additions & 0 deletions examples/with-graphql-hooks/lib/graphql-client.js
@@ -0,0 +1,40 @@
import { useMemo } from 'react'
import { GraphQLClient } from 'graphql-hooks'
import memCache from 'graphql-hooks-memcache'

let graphQLClient

function createClient(initialState) {
return new GraphQLClient({
ssrMode: typeof window === 'undefined',
url: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
cache: memCache({ initialState }),
})
}

export function initializeGraphQL(initialState = null) {
const _graphQLClient = graphQLClient ?? createClient(initialState)

// After navigating to a page with an initial GraphQL state, create a new cache with the
// current state merged with the incoming state and set it to the GraphQL client.
// This is necessary because the initial state of `memCache` can only be set once
if (initialState && graphQLClient) {
graphQLClient.cache = memCache({
initialState: Object.assign(
graphQLClient.cache.getInitialState(),
initialState
),
})
}
// For SSG and SSR always create a new GraphQL Client
if (typeof window === 'undefined') return _graphQLClient
// Create the GraphQL Client once in the client
if (!graphQLClient) graphQLClient = _graphQLClient

return _graphQLClient
}

export function useGraphQLClient(initialState) {
const store = useMemo(() => initializeGraphQL(initialState), [initialState])
return store
}
30 changes: 30 additions & 0 deletions examples/with-graphql-hooks/lib/graphql-request.js
@@ -0,0 +1,30 @@
const defaultOpts = { useCache: true }
/**
* Returns the result of a GraphQL query. It also adds the result to the
* cache of the GraphQL client for better initial data population in pages.
*
* Note: This helper tries to imitate what the query hooks of `graphql-hooks`
* do internally to make sure we generate the same cache key
*/
export default async function graphQLRequest(client, query, options) {
const opts = { ...defaultOpts, ...options }
const operation = {
query,
variables: opts.variables,
operationName: opts.operationName,
persisted: opts.persisted,
}

if (opts.persisted || (client.useGETForQueries && !opts.isMutation)) {
opts.fetchOptionsOverrides = {
...opts.fetchOptionsOverrides,
method: 'GET',
}
}

const cacheKey = client.getCacheKey(operation, opts)
const cacheValue = await client.request(operation, opts)

client.saveCache(cacheKey, cacheValue)
return cacheValue
}
27 changes: 0 additions & 27 deletions examples/with-graphql-hooks/lib/init-graphql.js

This file was deleted.

60 changes: 0 additions & 60 deletions examples/with-graphql-hooks/lib/with-graphql-client.js

This file was deleted.

7 changes: 0 additions & 7 deletions examples/with-graphql-hooks/package.json
@@ -1,25 +1,18 @@
{
"name": "with-graphql-hooks",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"author": "",
"license": "ISC",
"dependencies": {
"graphql-hooks": "^4.4.4",
"graphql-hooks-memcache": "^1.3.2",
"graphql-hooks-ssr": "^1.1.5",
"next": "latest",
"prop-types": "^15.7.2",
"react": "^16.8.2",
"react-dom": "^16.8.2"
},
"browser": {
"graphql-hooks-ssr": false
}
}
22 changes: 9 additions & 13 deletions examples/with-graphql-hooks/pages/_app.js
@@ -1,16 +1,12 @@
import App from 'next/app'
import withGraphQLClient from '../lib/with-graphql-client'
import { ClientContext } from 'graphql-hooks'
import { useGraphQLClient } from '../lib/graphql-client'

class MyApp extends App {
render() {
const { Component, pageProps, graphQLClient } = this.props
return (
<ClientContext.Provider value={graphQLClient}>
<Component {...pageProps} />
</ClientContext.Provider>
)
}
}
export default function App({ Component, pageProps }) {
const graphQLClient = useGraphQLClient(pageProps.initialGraphQLState)

export default withGraphQLClient(MyApp)
return (
<ClientContext.Provider value={graphQLClient}>
<Component {...pageProps} />
</ClientContext.Provider>
)
}
20 changes: 19 additions & 1 deletion examples/with-graphql-hooks/pages/index.js
@@ -1,6 +1,11 @@
import { initializeGraphQL } from '../lib/graphql-client'
import graphQLRequest from '../lib/graphql-request'
import App from '../components/app'
import Header from '../components/header'
import PostList from '../components/post-list'
import PostList, {
allPostsQuery,
allPostsQueryOptions,
} from '../components/post-list'

export default function Home() {
return (
Expand All @@ -10,3 +15,16 @@ export default function Home() {
</App>
)
}

export async function getStaticProps() {
const client = initializeGraphQL()

await graphQLRequest(client, allPostsQuery, allPostsQueryOptions())

return {
props: {
initialGraphQLState: client.cache.getInitialState(),
},
unstable_revalidate: 1,
}
}

0 comments on commit a00e85d

Please sign in to comment.