From 21d5aebd5335a8049fd3422e5a157f00ae31702a Mon Sep 17 00:00:00 2001 From: Ting-Hsiang Hsu Date: Sun, 25 Nov 2018 22:14:36 +0800 Subject: [PATCH] feat(example): add react-relay-network-modern example (#5349) In this example, we can: - `QueryRenderer` SSR - caching the data - use the feature of `react-relay-network-modern` which is the powerful tool for `relay-modern` I copy the example `with-relay-modern`, but I just modified the code. Some detail are not modified. If you think this example is needed, I will fix those. Otherwise, close this **PR** to let me know this example is not needed. --- .../with-react-relay-network-modern/.babelrc | 8 +++ examples/with-react-relay-network-modern/.env | 1 + .../.gitignore | 2 + .../.graphqlconfig | 8 +++ .../with-react-relay-network-modern/README.md | 70 +++++++++++++++++++ .../components/BlogPostPreview.js | 17 +++++ .../components/BlogPosts.js | 29 ++++++++ .../lib/createEnvironment/client.js | 36 ++++++++++ .../lib/createEnvironment/index.js | 6 ++ .../lib/createEnvironment/server.js | 35 ++++++++++ .../next.config.js | 17 +++++ .../package.json | 33 +++++++++ .../pages/_app.js | 59 ++++++++++++++++ .../pages/index.js | 31 ++++++++ .../schema/init-schema.graphql | 7 ++ 15 files changed, 359 insertions(+) create mode 100644 examples/with-react-relay-network-modern/.babelrc create mode 100644 examples/with-react-relay-network-modern/.env create mode 100644 examples/with-react-relay-network-modern/.gitignore create mode 100644 examples/with-react-relay-network-modern/.graphqlconfig create mode 100644 examples/with-react-relay-network-modern/README.md create mode 100644 examples/with-react-relay-network-modern/components/BlogPostPreview.js create mode 100644 examples/with-react-relay-network-modern/components/BlogPosts.js create mode 100644 examples/with-react-relay-network-modern/lib/createEnvironment/client.js create mode 100644 examples/with-react-relay-network-modern/lib/createEnvironment/index.js create mode 100644 examples/with-react-relay-network-modern/lib/createEnvironment/server.js create mode 100644 examples/with-react-relay-network-modern/next.config.js create mode 100644 examples/with-react-relay-network-modern/package.json create mode 100644 examples/with-react-relay-network-modern/pages/_app.js create mode 100644 examples/with-react-relay-network-modern/pages/index.js create mode 100644 examples/with-react-relay-network-modern/schema/init-schema.graphql diff --git a/examples/with-react-relay-network-modern/.babelrc b/examples/with-react-relay-network-modern/.babelrc new file mode 100644 index 0000000000..8fad94a215 --- /dev/null +++ b/examples/with-react-relay-network-modern/.babelrc @@ -0,0 +1,8 @@ +{ + "presets": [ + "next/babel" + ], + "plugins": [ + "relay" + ] +} \ No newline at end of file diff --git a/examples/with-react-relay-network-modern/.env b/examples/with-react-relay-network-modern/.env new file mode 100644 index 0000000000..d8a2a35d51 --- /dev/null +++ b/examples/with-react-relay-network-modern/.env @@ -0,0 +1 @@ +RELAY_ENDPOINT=https://api.graph.cool/relay/v1/next-js-with-relay-modern-example diff --git a/examples/with-react-relay-network-modern/.gitignore b/examples/with-react-relay-network-modern/.gitignore new file mode 100644 index 0000000000..748d4f6e39 --- /dev/null +++ b/examples/with-react-relay-network-modern/.gitignore @@ -0,0 +1,2 @@ +__generated__/ +schema/schema.graphql \ No newline at end of file diff --git a/examples/with-react-relay-network-modern/.graphqlconfig b/examples/with-react-relay-network-modern/.graphqlconfig new file mode 100644 index 0000000000..fadb4e0802 --- /dev/null +++ b/examples/with-react-relay-network-modern/.graphqlconfig @@ -0,0 +1,8 @@ +{ + "schemaPath": "schema/schema.graphql", + "extensions": { + "endpoints": { + "dev": "https://api.graph.cool/relay/v1/next-js-with-relay-modern-example" + } + } +} \ No newline at end of file diff --git a/examples/with-react-relay-network-modern/README.md b/examples/with-react-relay-network-modern/README.md new file mode 100644 index 0000000000..c4c18c891a --- /dev/null +++ b/examples/with-react-relay-network-modern/README.md @@ -0,0 +1,70 @@ +[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-relay-modern) + +# Relay Modern Example + +## How to use + +### Using `create-next-app` + +Execute [`create-next-app`](https://github.com/segmentio/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example: + +```bash +npx create-next-app --example with-relay-modern with-relay-modern-app +# or +yarn create next-app --example with-relay-modern with-relay-modern-app +``` + +### Download manually + +Download the example: + +```bash +curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-relay-modern +cd with-relay-modern +``` + +Install it: + +```bash +npm install +# or +yarn +``` + +Download schema introspection data from configured Relay endpoint + +```bash +npm run schema +# or +yarn schema +``` + +Run Relay ahead-of-time compilation (should be re-run after any edits to components that query data with Relay) + +```bash +npm run relay +# or +yarn relay +``` + +Run the project + +```bash +npm run dev +# or +yarn dev +``` + +Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)): + +```bash +now +``` + +## The idea behind the example + +[Relay Modern](https://facebook.github.io/relay/docs/relay-modern.html) 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. diff --git a/examples/with-react-relay-network-modern/components/BlogPostPreview.js b/examples/with-react-relay-network-modern/components/BlogPostPreview.js new file mode 100644 index 0000000000..4043ebcc5b --- /dev/null +++ b/examples/with-react-relay-network-modern/components/BlogPostPreview.js @@ -0,0 +1,17 @@ +import React from 'react' +import { createFragmentContainer, graphql } from 'react-relay' + +const BlogPostPreview = props => { + return ( +
{props.post.title}
+ ) +} + +export default createFragmentContainer(BlogPostPreview, { + post: graphql` + fragment BlogPostPreview_post on BlogPost { + id + title + } + ` +}) diff --git a/examples/with-react-relay-network-modern/components/BlogPosts.js b/examples/with-react-relay-network-modern/components/BlogPosts.js new file mode 100644 index 0000000000..0d1f54e5cd --- /dev/null +++ b/examples/with-react-relay-network-modern/components/BlogPosts.js @@ -0,0 +1,29 @@ +import React from 'react' +import { createFragmentContainer, graphql } from 'react-relay' +import BlogPostPreview from './BlogPostPreview' + +const BlogPosts = props => { + return ( +
+

Blog posts

+ {props.viewer.allBlogPosts.edges.map(({ node }) => + + )} +
+ ) +} + +export default createFragmentContainer(BlogPosts, { + viewer: graphql` + fragment BlogPosts_viewer on Viewer { + allBlogPosts(first: 10, orderBy: createdAt_DESC) { + edges { + node { + ...BlogPostPreview_post + id + } + } + } + } + ` +}) diff --git a/examples/with-react-relay-network-modern/lib/createEnvironment/client.js b/examples/with-react-relay-network-modern/lib/createEnvironment/client.js new file mode 100644 index 0000000000..d6003620df --- /dev/null +++ b/examples/with-react-relay-network-modern/lib/createEnvironment/client.js @@ -0,0 +1,36 @@ +import { + RelayNetworkLayer, + cacheMiddleware, + urlMiddleware +} from 'react-relay-network-modern/node8' +import RelaySSR from 'react-relay-network-modern-ssr/node8/client' +import { Environment, RecordSource, Store } from 'relay-runtime' + +const source = new RecordSource() +const store = new Store(source) + +let storeEnvironment = null + +export default { + createEnvironment: relayData => { + if (storeEnvironment) return storeEnvironment + + storeEnvironment = new Environment({ + store, + network: new RelayNetworkLayer([ + cacheMiddleware({ + size: 100, + ttl: 60 * 1000 + }), + new RelaySSR(relayData).getMiddleware({ + lookup: false + }), + urlMiddleware({ + url: req => process.env.RELAY_ENDPOINT + }) + ]) + }) + + return storeEnvironment + } +} diff --git a/examples/with-react-relay-network-modern/lib/createEnvironment/index.js b/examples/with-react-relay-network-modern/lib/createEnvironment/index.js new file mode 100644 index 0000000000..ce62903a7d --- /dev/null +++ b/examples/with-react-relay-network-modern/lib/createEnvironment/index.js @@ -0,0 +1,6 @@ +import 'isomorphic-fetch' + +export const { initEnvironment, createEnvironment } = (!process.browser + ? require('./server') + : require('./client') +).default diff --git a/examples/with-react-relay-network-modern/lib/createEnvironment/server.js b/examples/with-react-relay-network-modern/lib/createEnvironment/server.js new file mode 100644 index 0000000000..d25355fe3e --- /dev/null +++ b/examples/with-react-relay-network-modern/lib/createEnvironment/server.js @@ -0,0 +1,35 @@ +import { RelayNetworkLayer, urlMiddleware } from 'react-relay-network-modern/node8' +import RelaySSR from 'react-relay-network-modern-ssr/node8/server' +import { Network, Environment, RecordSource, Store } from 'relay-runtime' + +export default { + initEnvironment: () => { + const source = new RecordSource() + const store = new Store(source) + const relaySSR = new RelaySSR() + + return { + relaySSR, + environment: new Environment({ + store, + network: new RelayNetworkLayer([ + urlMiddleware({ + url: req => process.env.RELAY_ENDPOINT + }), + relaySSR.getMiddleware() + ]) + }) + } + }, + createEnvironment: (relayData, key) => { + const source = new RecordSource() + const store = new Store(source) + + return new Environment({ + store, + network: Network.create( + () => relayData.find(([dataKey]) => dataKey === key)[1] + ) + }) + } +} diff --git a/examples/with-react-relay-network-modern/next.config.js b/examples/with-react-relay-network-modern/next.config.js new file mode 100644 index 0000000000..52e2ceea7d --- /dev/null +++ b/examples/with-react-relay-network-modern/next.config.js @@ -0,0 +1,17 @@ +require('dotenv').config() + +const path = require('path') +const Dotenv = require('dotenv-webpack') + +module.exports = { + webpack: (config, { isServer }) => { + config.plugins.push( + new Dotenv({ + path: path.join(__dirname, '.env'), + systemvars: true + }) + ) + + return config + } +} diff --git a/examples/with-react-relay-network-modern/package.json b/examples/with-react-relay-network-modern/package.json new file mode 100644 index 0000000000..33e93bad23 --- /dev/null +++ b/examples/with-react-relay-network-modern/package.json @@ -0,0 +1,33 @@ +{ + "name": "with-react-relay-network-modern", + "version": "3.0.3", + "description": "Example of Next.js with Relay Modern and react-relay-network-modern", + "scripts": { + "graphcool-init": "graphcool init --schema schema/init-schema.graphql", + "dev": "next", + "build": "next build", + "start": "next start", + "relay": "relay-compiler --src ./ --exclude '**/.next/**' '**/node_modules/**' '**/test/**' '**/__generated__/**' --exclude '**/schema/**' --schema ./schema/schema.graphql", + "schema": "graphql get-schema dev" + }, + "author": "", + "license": "ISC", + "dependencies": { + "dotenv": "^4.0.0", + "dotenv-webpack": "^1.5.4", + "graphql": "^0.13.2", + "isomorphic-fetch": "2.2.1", + "next": "latest", + "react": "^16.2.0", + "react-dom": "^16.2.0", + "react-relay": "^1.5.0", + "react-relay-network-modern": "2.4.0", + "react-relay-network-modern-ssr": "1.2.1" + }, + "devDependencies": { + "babel-plugin-relay": "^1.4.1", + "graphcool": "^1.2.1", + "graphql-cli": "^1.0.0-beta.4", + "relay-compiler": "^1.5.0" + } +} diff --git a/examples/with-react-relay-network-modern/pages/_app.js b/examples/with-react-relay-network-modern/pages/_app.js new file mode 100644 index 0000000000..4d28ed9bb1 --- /dev/null +++ b/examples/with-react-relay-network-modern/pages/_app.js @@ -0,0 +1,59 @@ +import React from 'react' +import { QueryRenderer, fetchQuery } from 'react-relay' +import NextApp, { Container } from 'next/app' + +import { + initEnvironment, + createEnvironment +} from '../lib/createEnvironment' + +export default class App extends NextApp { + static getInitialProps = async ({ Component, router, ctx }) => { + const { variables } = Component.getInitialProps ? await Component.getInitialProps(ctx) : {} + + try { + if (initEnvironment) { + const { environment, relaySSR } = initEnvironment() + + await fetchQuery(environment, Component.query, variables) + + return { + variables, + relayData: await relaySSR.getCache() + } + } + } catch (e) { + console.log(e) + } + + return { + variables + } + }; + + render () { + const { Component, variables = {}, relayData } = this.props + const environment = createEnvironment( + relayData, + JSON.stringify({ + queryID: Component.query ? Component.query().name : undefined, + variables + }) + ) + + return ( + + { + if (error) return
{error.message}
+ else if (props) return + return
Loading
+ }} + /> +
+ ) + } +} diff --git a/examples/with-react-relay-network-modern/pages/index.js b/examples/with-react-relay-network-modern/pages/index.js new file mode 100644 index 0000000000..062d950635 --- /dev/null +++ b/examples/with-react-relay-network-modern/pages/index.js @@ -0,0 +1,31 @@ +import React, { Component } from 'react' +import { graphql } from 'react-relay' +import BlogPosts from '../components/BlogPosts' + +export default class Index extends Component { + /** + * if you need to use variables + * + * static getInitialProps = async () => ({ + * variables: { + * key: 'value', + * }, + * }); + */ + + static query = graphql` + query pages_indexQuery { + viewer { + ...BlogPosts_viewer + } + } + `; + + render () { + return ( +
+ +
+ ) + } +} diff --git a/examples/with-react-relay-network-modern/schema/init-schema.graphql b/examples/with-react-relay-network-modern/schema/init-schema.graphql new file mode 100644 index 0000000000..c7c5711d28 --- /dev/null +++ b/examples/with-react-relay-network-modern/schema/init-schema.graphql @@ -0,0 +1,7 @@ +type BlogPost implements Node { + content: String! + createdAt: DateTime! + id: ID! @isUnique + title: String! + updatedAt: DateTime! +} \ No newline at end of file -- GitLab