From c5e1422f762a9dc003cf3827d8c1da50d5a3ec00 Mon Sep 17 00:00:00 2001 From: Jovi De Croock Date: Wed, 28 Apr 2021 14:18:14 +0200 Subject: [PATCH] (examples) - next.js (#1502) * add next example * Update examples/with-next/README.md Co-authored-by: Jess Telford * Update static.js * convert to trygql.dev * remove unused dep * use gql helper * Remove lockfile Co-authored-by: Jess Telford --- examples/with-next/README.md | 48 ++++++++++++++++++++++++++ examples/with-next/package.json | 17 ++++++++++ examples/with-next/pages/_app.js | 10 ++++++ examples/with-next/pages/index.js | 40 ++++++++++++++++++++++ examples/with-next/pages/server.js | 54 ++++++++++++++++++++++++++++++ examples/with-next/pages/static.js | 54 ++++++++++++++++++++++++++++++ 6 files changed, 223 insertions(+) create mode 100644 examples/with-next/README.md create mode 100644 examples/with-next/package.json create mode 100644 examples/with-next/pages/_app.js create mode 100644 examples/with-next/pages/index.js create mode 100644 examples/with-next/pages/server.js create mode 100644 examples/with-next/pages/static.js diff --git a/examples/with-next/README.md b/examples/with-next/README.md new file mode 100644 index 0000000000..bb29b2b2a5 --- /dev/null +++ b/examples/with-next/README.md @@ -0,0 +1,48 @@ +# Integrating with Next + +## getInitialProps + +This is the output you'll get when you're using `{ ssr: true }`, this way urql will try to automate +as much for you as it possibly can by using a [`prepass`](https://github.com/FormidableLabs/react-ssr-prepass) +this means that every `useQuery` used in your virtual-dom will be ran, the data will be collected on the server +and hydrated on the client. + +> NOTE: to reduce performance complexities try to keep this to top-level renders as this can amount to waterfalls. + +## getStaticProps + +This requires some manual work, when we look at [`static.js`](./pages/static.js) we can see that we define our own +`getStaticProps` method, this because these methods are only `user-facing`. When doing a `yarn next build` we'll need to +ensure that the server we're targetting is running so we can successfully execute the static prerender. + +## getServerSideProps + +This requires some manual work, when we look at [`server.js`](./pages/server.js) we can see that we define our own +`getServerSideProps` method, this because these methods are only `user-facing`. + +## Output + +We can see that our `/` and `/server` routes are rendered on the server and `/static` is statically prerendered. + +``` +Page Size First Load JS +┌ λ / 4.98 kB 90 kB +├ /_app 0 B 85 kB +├ ○ /404 3.46 kB 88.5 kB +├ λ /api/graphql 0 B 85 kB +├ λ /server 878 B 85.9 kB +└ ● /static 895 B 85.9 kB ++ First Load JS shared by all 85 kB + ├ chunks/d8c192fcf6e34535672c13f111ef41e3832b265d.d03071.js 17.4 kB + ├ chunks/f6078781a05fe1bcb0902d23dbbb2662c8d200b3.6a2b27.js 13.3 kB + ├ chunks/framework.4b1bec.js 41.8 kB + ├ chunks/main.3d1d43.js 7.14 kB + ├ chunks/pages/_app.92bde8.js 4.68 kB + └ chunks/webpack.50bee0.js 751 B + + +λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps) +○ (Static) automatically rendered as static HTML (uses no initial props) +● (SSG) automatically generated as static HTML + JSON (uses getStaticProps) + (ISR) incremental static regeneration (uses revalidate in getStaticProps) +``` diff --git a/examples/with-next/package.json b/examples/with-next/package.json new file mode 100644 index 0000000000..d8dcc24dfc --- /dev/null +++ b/examples/with-next/package.json @@ -0,0 +1,17 @@ +{ + "name": "next-get-static-props", + "version": "1.0.0", + "private": true, + "dependencies": { + "graphql": "^15.5.0", + "next": "10.1.2", + "next-urql": "^3.0.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "urql": "^2.0.2" + }, + "scripts": { + "start": "next", + "build": "next build" + } +} diff --git a/examples/with-next/pages/_app.js b/examples/with-next/pages/_app.js new file mode 100644 index 0000000000..4c753e273c --- /dev/null +++ b/examples/with-next/pages/_app.js @@ -0,0 +1,10 @@ +import { withUrqlClient } from "next-urql"; + +const App = ({ Component, pageProps }) => ; + +export default withUrqlClient( + () => ({ + url: "https://trygql.dev/graphql/basic-pokedex" + }), + { ssr: false } +)(App); diff --git a/examples/with-next/pages/index.js b/examples/with-next/pages/index.js new file mode 100644 index 0000000000..71c3936666 --- /dev/null +++ b/examples/with-next/pages/index.js @@ -0,0 +1,40 @@ +import { initUrqlClient, withUrqlClient } from "next-urql"; +import { + ssrExchange, + dedupExchange, + cacheExchange, + fetchExchange, + useQuery, + gql +} from "urql"; + +const POKEMONS_QUERY = gql` + query { + pokemons(limit: 10) { + id + name + } + } +`; + +function Index() { + const [res] = useQuery({ query: POKEMONS_QUERY }); + + return ( +
+

Static

+ {res.data.pokemons.map((pokemon) => ( +
+ {pokemon.id} - {pokemon.name} +
+ ))} +
+ ); +} + +export default withUrqlClient( + () => ({ + url: "https://trygql.dev/graphql/basic-pokedex" + }), + { ssr: true } +)(Index); diff --git a/examples/with-next/pages/server.js b/examples/with-next/pages/server.js new file mode 100644 index 0000000000..8844f6a450 --- /dev/null +++ b/examples/with-next/pages/server.js @@ -0,0 +1,54 @@ +import { initUrqlClient } from "next-urql"; +import { + ssrExchange, + dedupExchange, + cacheExchange, + fetchExchange, + useQuery + gql +} from "urql"; + +const POKEMONS_QUERY = gql` + query { + pokemons(limit: 10) { + id + name + } + } +`; + +function Server() { + const [res] = useQuery({ query: POKEMONS_QUERY }); + + return ( +
+

Server-side render

+ {res.data.pokemons.map((pokemon) => ( +
+ {pokemon.id} - {pokemon.name} +
+ ))} +
+ ); +} + +export async function getServerSideProps() { + const ssrCache = ssrExchange({ isClient: false }); + const client = initUrqlClient({ + url: "https://trygql.dev/graphql/basic-pokedex", + exchanges: [dedupExchange, cacheExchange, ssrCache, fetchExchange] + }, false); + + // This query is used to populate the cache for the query + // used on this page. + await client.query(POKEMONS_QUERY).toPromise(); + + return { + props: { + // urqlState is a keyword here so withUrqlClient can pick it up. + urqlState: ssrCache.extractData() + }, + }; +} + +export default Server; diff --git a/examples/with-next/pages/static.js b/examples/with-next/pages/static.js new file mode 100644 index 0000000000..bf7569cb70 --- /dev/null +++ b/examples/with-next/pages/static.js @@ -0,0 +1,54 @@ +import { initUrqlClient } from "next-urql"; +import { + ssrExchange, + dedupExchange, + cacheExchange, + fetchExchange, + useQuery, + gql +} from "urql"; + +const POKEMONS_QUERY = gql` + query { + pokemons(limit: 10) { + id + name + } + } +`; + +function Static() { + const [res] = useQuery({ query: POKEMONS_QUERY }); + + return ( +
+

Static

+ {res.data.pokemons.map((pokemon) => ( +
+ {pokemon.id} - {pokemon.name} +
+ ))} +
+ ); +} + +export async function getStaticProps() { + const ssrCache = ssrExchange({ isClient: false }); + const client = initUrqlClient({ + url: "https://trygql.dev/graphql/basic-pokedex", + exchanges: [dedupExchange, cacheExchange, ssrCache, fetchExchange] + }, false); + + // This query is used to populate the cache for the query + // used on this page. + await client.query(POKEMONS_QUERY).toPromise(); + + return { + props: { + // urqlState is a keyword here so withUrqlClient can pick it up. + urqlState: ssrCache.extractData() + }, + }; +} + +export default Static;