From 2c21682a61a03ae2bc5d8ebbb355310fa80915a4 Mon Sep 17 00:00:00 2001 From: Luis Alvarez D Date: Mon, 8 Jun 2020 10:41:47 -0500 Subject: [PATCH] [Examples] Move with-relay-modern to SSG (#13882) Related to https://github.com/vercel/next.js/issues/11014 --- examples/with-relay-modern/.env | 2 +- examples/with-relay-modern/README.md | 2 - .../components/BlogPostPreview.js | 1 - .../with-relay-modern/components/BlogPosts.js | 1 - .../lib/createRelayEnvironment.js | 44 --------------- examples/with-relay-modern/lib/relay.js | 50 +++++++++++++++++ examples/with-relay-modern/lib/withData.js | 54 ------------------- examples/with-relay-modern/next.config.js | 22 -------- examples/with-relay-modern/package.json | 7 +-- examples/with-relay-modern/pages/_app.js | 12 +++++ examples/with-relay-modern/pages/about.js | 12 +++++ examples/with-relay-modern/pages/index.js | 24 +++++++-- 12 files changed, 97 insertions(+), 134 deletions(-) delete mode 100644 examples/with-relay-modern/lib/createRelayEnvironment.js create mode 100644 examples/with-relay-modern/lib/relay.js delete mode 100644 examples/with-relay-modern/lib/withData.js delete mode 100644 examples/with-relay-modern/next.config.js create mode 100644 examples/with-relay-modern/pages/_app.js create mode 100644 examples/with-relay-modern/pages/about.js diff --git a/examples/with-relay-modern/.env b/examples/with-relay-modern/.env index d8a2a35d5119c..01cf99e71fceb 100644 --- a/examples/with-relay-modern/.env +++ b/examples/with-relay-modern/.env @@ -1 +1 @@ -RELAY_ENDPOINT=https://api.graph.cool/relay/v1/next-js-with-relay-modern-example +NEXT_PUBLIC_RELAY_ENDPOINT=https://api.graph.cool/relay/v1/next-js-with-relay-modern-example diff --git a/examples/with-relay-modern/README.md b/examples/with-relay-modern/README.md index 65fbf2d087951..ed6ed9a969435 100644 --- a/examples/with-relay-modern/README.md +++ b/examples/with-relay-modern/README.md @@ -2,8 +2,6 @@ [Relay Modern](https://relay.dev/) is a new version of Relay designed from the ground up to be easier to use, more extensible and, most of all, able to improve performance on mobile devices. Relay Modern accomplishes this with static queries and ahead-of-time code generation. -In this simple example, we integrate Relay Modern seamlessly with Next by wrapping our _pages_ inside a [higher-order component (HOC)](https://facebook.github.io/react/docs/higher-order-components.html). Using the HOC pattern we're able to pass down a query result data created by Relay into our React component hierarchy defined inside each page of our Next application. The HOC takes `options` argument that allows to specify a `query` that will be executed on the server when a page is being loaded. - This example relies on [graph.cool](https://www.graph.cool) for its GraphQL backend. ## Deploy your own diff --git a/examples/with-relay-modern/components/BlogPostPreview.js b/examples/with-relay-modern/components/BlogPostPreview.js index 7e1324e58dd87..4988b2290312d 100644 --- a/examples/with-relay-modern/components/BlogPostPreview.js +++ b/examples/with-relay-modern/components/BlogPostPreview.js @@ -1,4 +1,3 @@ -import React from 'react' import { createFragmentContainer, graphql } from 'react-relay' const BlogPostPreview = ({ post }) =>
  • {post.title}
  • diff --git a/examples/with-relay-modern/components/BlogPosts.js b/examples/with-relay-modern/components/BlogPosts.js index 834b5971d8749..2cd0b78a0e0eb 100644 --- a/examples/with-relay-modern/components/BlogPosts.js +++ b/examples/with-relay-modern/components/BlogPosts.js @@ -1,4 +1,3 @@ -import React from 'react' import { createFragmentContainer, graphql } from 'react-relay' import BlogPostPreview from './BlogPostPreview' diff --git a/examples/with-relay-modern/lib/createRelayEnvironment.js b/examples/with-relay-modern/lib/createRelayEnvironment.js deleted file mode 100644 index d152c0d44e13e..0000000000000 --- a/examples/with-relay-modern/lib/createRelayEnvironment.js +++ /dev/null @@ -1,44 +0,0 @@ -import { Environment, Network, RecordSource, Store } from 'relay-runtime' - -let relayEnvironment = null - -// Define a function that fetches the results of an operation (query/mutation/etc) -// and returns its results as a Promise: -function fetchQuery(operation, variables, cacheConfig, uploadables) { - return fetch(process.env.RELAY_ENDPOINT, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, // Add authentication and other headers here - body: JSON.stringify({ - query: operation.text, // GraphQL text from input - variables, - }), - }).then((response) => response.json()) -} - -export default function initEnvironment({ records = {} } = {}) { - // Create a network layer from the fetch function - const network = Network.create(fetchQuery) - const store = new Store(new RecordSource(records)) - - // Make sure to create a new Relay environment for every server-side request so that data - // isn't shared between connections (which would be bad) - if (typeof window === 'undefined') { - return new Environment({ - network, - store, - }) - } - - // reuse Relay environment on client-side - if (!relayEnvironment) { - relayEnvironment = new Environment({ - network, - store, - }) - } - - return relayEnvironment -} diff --git a/examples/with-relay-modern/lib/relay.js b/examples/with-relay-modern/lib/relay.js new file mode 100644 index 0000000000000..4f91b008a6b10 --- /dev/null +++ b/examples/with-relay-modern/lib/relay.js @@ -0,0 +1,50 @@ +import { useMemo } from 'react' +import { Environment, Network, RecordSource, Store } from 'relay-runtime' + +let relayEnvironment + +// Define a function that fetches the results of an operation (query/mutation/etc) +// and returns its results as a Promise +function fetchQuery(operation, variables, cacheConfig, uploadables) { + return fetch(process.env.NEXT_PUBLIC_RELAY_ENDPOINT, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, // Add authentication and other headers here + body: JSON.stringify({ + query: operation.text, // GraphQL text from input + variables, + }), + }).then((response) => response.json()) +} + +function createEnvironment(initialRecords) { + return new Environment({ + // Create a network layer from the fetch function + network: Network.create(fetchQuery), + store: new Store(new RecordSource()), + }) +} + +export function initEnvironment(initialRecords) { + // Create a network layer from the fetch function + const environment = relayEnvironment ?? createEnvironment(initialRecords) + + // If your page has Next.js data fetching methods that use Relay, the initial records + // will get hydrated here + if (initialRecords) { + environment.getStore().publish(new RecordSource(initialRecords)) + } + // For SSG and SSR always create a new Relay environment + if (typeof window === 'undefined') return environment + // Create the Relay environment once in the client + if (!relayEnvironment) relayEnvironment = environment + + return relayEnvironment +} + +export function useEnvironment(initialRecords) { + const store = useMemo(() => initEnvironment(initialRecords), [initialRecords]) + return store +} diff --git a/examples/with-relay-modern/lib/withData.js b/examples/with-relay-modern/lib/withData.js deleted file mode 100644 index 9dd27fecda84c..0000000000000 --- a/examples/with-relay-modern/lib/withData.js +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react' -import initEnvironment from './createRelayEnvironment' -import { fetchQuery, ReactRelayContext } from 'react-relay' - -export default function withData(ComposedComponent, options = {}) { - return class WithData extends React.Component { - static displayName = `WithData(${ComposedComponent.displayName})` - - static async getInitialProps(ctx) { - // Evaluate the composed component's getInitialProps() - let composedInitialProps = {} - if (ComposedComponent.getInitialProps) { - composedInitialProps = await ComposedComponent.getInitialProps(ctx) - } - - let queryProps = {} - let queryRecords = {} - const environment = initEnvironment() - - if (options.query) { - // Provide the `url` prop data in case a graphql query uses it - // const url = { query: ctx.query, pathname: ctx.pathname } - const variables = {} - // TODO: Consider RelayQueryResponseCache - // https://github.com/facebook/relay/issues/1687#issuecomment-302931855 - queryProps = await fetchQuery(environment, options.query, variables) - queryRecords = environment.getStore().getSource().toJSON() - } - - return { - ...composedInitialProps, - ...queryProps, - queryRecords, - } - } - - constructor(props) { - super(props) - this.environment = initEnvironment({ - records: props.queryRecords, - }) - } - - render() { - return ( - - - - ) - } - } -} diff --git a/examples/with-relay-modern/next.config.js b/examples/with-relay-modern/next.config.js deleted file mode 100644 index 12aabac545184..0000000000000 --- a/examples/with-relay-modern/next.config.js +++ /dev/null @@ -1,22 +0,0 @@ -require('dotenv').config() - -const path = require('path') -const Dotenv = require('dotenv-webpack') - -module.exports = { - webpack: (config) => { - config.plugins = config.plugins || [] - - config.plugins = [ - ...config.plugins, - - // Read the .env file - new Dotenv({ - path: path.join(__dirname, '.env'), - systemvars: true, - }), - ] - - return config - }, -} diff --git a/examples/with-relay-modern/package.json b/examples/with-relay-modern/package.json index 3dce58a477cb5..4c18737b706ee 100644 --- a/examples/with-relay-modern/package.json +++ b/examples/with-relay-modern/package.json @@ -1,7 +1,7 @@ { "name": "with-relay-modern", - "version": "3.0.4", - "description": "Example of Next.js with Relay Modern SSR", + "version": "1.0.0", + "description": "Example of Next.js with Relay Modern", "scripts": { "graphcool-init": "graphcool init --schema schema/init-schema.graphql", "dev": "next", @@ -10,11 +10,8 @@ "relay": "relay-compiler --src ./ --exclude '**/.next/**' '**/node_modules/**' '**/test/**' '**/__generated__/**' --exclude '**/schema/**' --schema ./schema/schema.graphql", "schema": "graphql get-schema -e dev" }, - "author": "", "license": "ISC", "dependencies": { - "dotenv": "^8.2.0", - "dotenv-webpack": "^1.7.0", "graphql": "^14.6.0", "next": "latest", "react": "^16.13.0", diff --git a/examples/with-relay-modern/pages/_app.js b/examples/with-relay-modern/pages/_app.js new file mode 100644 index 0000000000000..838595f393e65 --- /dev/null +++ b/examples/with-relay-modern/pages/_app.js @@ -0,0 +1,12 @@ +import { ReactRelayContext } from 'react-relay' +import { useEnvironment } from '../lib/relay' + +export default function App({ Component, pageProps }) { + const environment = useEnvironment(pageProps.initialRecords) + + return ( + + + + ) +} diff --git a/examples/with-relay-modern/pages/about.js b/examples/with-relay-modern/pages/about.js new file mode 100644 index 0000000000000..3b24db70d9963 --- /dev/null +++ b/examples/with-relay-modern/pages/about.js @@ -0,0 +1,12 @@ +import Link from 'next/link' + +export default function About() { + return ( +
    + + Home + +

    This is the about page

    +
    + ) +} diff --git a/examples/with-relay-modern/pages/index.js b/examples/with-relay-modern/pages/index.js index 16ac90b68471e..bec104d03a543 100644 --- a/examples/with-relay-modern/pages/index.js +++ b/examples/with-relay-modern/pages/index.js @@ -1,13 +1,29 @@ -import withData from '../lib/withData' +import Link from 'next/link' +import { fetchQuery } from 'react-relay' +import { initEnvironment } from '../lib/relay' import BlogPosts from '../components/BlogPosts' import indexPageQuery from '../queries/indexPage' const Index = ({ viewer }) => (
    + + About +
    ) -export default withData(Index, { - query: indexPageQuery, -}) +export async function getStaticProps() { + const environment = initEnvironment() + const queryProps = await fetchQuery(environment, indexPageQuery) + const initialRecords = environment.getStore().getSource().toJSON() + + return { + props: { + ...queryProps, + initialRecords, + }, + } +} + +export default Index