diff --git a/README.md b/README.md index 356e6dd..aa93c4c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ You can easily use `Query` component to send queries to the GraphQL server. Just ``` -Use the `withApollo` HOC to wrap your pages. +Use the `withData` HOC to wrap your pages. ```jsx // ./frontend/pages/articles.js @@ -32,7 +32,7 @@ import Layout from '../components/Layout/Layout' import SEO from '../components/SEO/SEO' import Query from '../components/Query/query' import ARTICLES_QUERY from '../apollo/queries/articles/articles'; -import { withApollo } from '../lib/apollo'; +import withData from '../lib/apollo'; const Articles = () => { diff --git a/components/SEO/SEO.js b/components/SEO/SEO.js index 0946959..c7fe044 100644 --- a/components/SEO/SEO.js +++ b/components/SEO/SEO.js @@ -10,6 +10,10 @@ const SEO = props => { return ( {pageTitle} + ) } diff --git a/lib/apollo.js b/lib/apollo.js index 97ab4dd..36a7660 100644 --- a/lib/apollo.js +++ b/lib/apollo.js @@ -1,132 +1,13 @@ -import React, { useMemo } from 'react' -import Head from 'next/head' -import { ApolloProvider } from '@apollo/react-hooks' -import { ApolloClient, InMemoryCache, HttpLink } from 'apollo-boost' -import fetch from 'isomorphic-unfetch' +import { withData } from 'next-apollo' +import { HttpLink } from 'apollo-boost' -const GRAPHQL_URL = process.env.BACKEND_URL || 'http://localhost:3000/api/graphql' - -let apolloClient = null - -/** - * Creates and provides the apolloContext - * to a next.js PageTree. Use it by wrapping - * your PageComponent via HOC pattern. - * @param {Function|Class} PageComponent - * @param {Object} [config] - * @param {Boolean} [config.ssr=true] - */ -export function withApollo (PageComponent, { ssr = true } = {}) { - const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => { - const client = useMemo( - () => apolloClient || initApolloClient(apolloState), - [] - ) - return ( - - - - ) - } - - // Set the correct displayName in development - if (process.env.NODE_ENV !== 'production') { - const displayName = - PageComponent.displayName || PageComponent.name || 'Component' - - if (displayName === 'App') { - console.warn('This withApollo HOC only works with PageComponents.') +const config = { + link: new HttpLink({ + uri: 'http://localhost:3000/api/graphql', + opts: { + credentials: 'same-origin' } - - WithApollo.displayName = `withApollo(${displayName})` - } - - // Allow Next.js to remove getInitialProps from the browser build - if (typeof window === 'undefined') { - if (ssr) { - WithApollo.getInitialProps = async ctx => { - const { AppTree } = ctx - - let pageProps = {} - if (PageComponent.getInitialProps) { - pageProps = await PageComponent.getInitialProps(ctx) - } - - // Run all GraphQL queries in the component tree - // and extract the resulting data - const apolloClient = initApolloClient() - - try { - // Run all GraphQL queries - await require('@apollo/react-ssr').getDataFromTree( - - ) - } catch (error) { - // Prevent Apollo Client GraphQL errors from crashing SSR. - // Handle them in components via the data.error prop: - // https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error - console.error('Error while running `getDataFromTree`', error) - } - - // getDataFromTree does not call componentWillUnmount - // head side effect therefore need to be cleared manually - Head.rewind() - - // Extract query data from the Apollo store - const apolloState = apolloClient.cache.extract() - - return { - ...pageProps, - apolloState - } - } - } - } - - return WithApollo -} - -/** - * Always creates a new apollo client on the server - * Creates or reuses apollo client in the browser. - * @param {Object} initialState - */ -function initApolloClient (initialState) { - // Make sure to create a new client for every server-side request so that data - // isn't shared between connections (which would be bad) - if (typeof window === 'undefined') { - return createApolloClient(initialState) - } - - // Reuse client on the client-side - if (!apolloClient) { - apolloClient = createApolloClient(initialState) - } - - return apolloClient -} - -/** - * Creates and configures the ApolloClient - * @param {Object} [initialState={}] - */ -function createApolloClient (initialState = {}) { - // Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient - const isBrowser = typeof window !== 'undefined' - return new ApolloClient({ - connectToDevTools: isBrowser, - ssrMode: !isBrowser, // Disables forceFetch on the server (so queries are only run once) - link: new HttpLink({ - uri: GRAPHQL_URL, // Server URL (must be absolute) - credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers` - // Use fetch() polyfill on the server - fetch: !isBrowser && fetch - }), - cache: new InMemoryCache().restore(initialState) }) } + +export default withData(config) diff --git a/package-lock.json b/package-lock.json index 94c664a..aed6789 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2038,6 +2038,16 @@ "sprintf-js": "~1.0.2" } }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, "arity-n": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", @@ -2148,6 +2158,12 @@ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.2.tgz", "integrity": "sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA==" }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -2294,6 +2310,12 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" }, + "axobject-query": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.1.2.tgz", + "integrity": "sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ==", + "dev": true + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -4146,6 +4168,12 @@ "type": "^1.0.1" } }, + "damerau-levenshtein": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", + "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", + "dev": true + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -5305,6 +5333,31 @@ } } }, + "eslint-plugin-jsx-a11y": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz", + "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.5", + "aria-query": "^3.0.0", + "array-includes": "^3.0.3", + "ast-types-flow": "^0.0.7", + "axobject-query": "^2.0.2", + "damerau-levenshtein": "^1.0.4", + "emoji-regex": "^7.0.2", + "has": "^1.0.3", + "jsx-ast-utils": "^2.2.1" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + } + } + }, "eslint-plugin-node": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.0.0.tgz", @@ -8161,6 +8214,18 @@ } } }, + "next-apollo": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/next-apollo/-/next-apollo-3.1.10.tgz", + "integrity": "sha512-VX64IW7h6Rdg+Ma0y8ryAIz7IfyLaz6Nd4VITuhfXFZ3nj51blcjRR6kc4E945SPk2WdrIMlLRZubgPRAgh9Ug==", + "requires": { + "@babel/runtime": "^7.4.5", + "isomorphic-unfetch": "^3.0.0", + "prop-types": "15.7.2", + "prop-types-exact": "1.2.0", + "url": "0.11.0" + } + }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", diff --git a/package.json b/package.json index a8d28e5..b0c915a 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "graphql-tag": "^2.10.3", "isomorphic-unfetch": "^3.0.0", "next": "9.2.2", + "next-apollo": "^3.1.10", "next-with-apollo": "^5.0.0", "node-sass": "^4.13.1", "optimize-css-assets-webpack-plugin": "^5.0.3", @@ -27,6 +28,7 @@ "eslint-config-node": "^4.0.0", "eslint-config-standard-jsx": "^8.1.0", "eslint-plugin-import": "^2.20.1", + "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-node": "^11.0.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-react": "^7.18.3", diff --git a/pages/articles.js b/pages/articles.js index 5560b3d..31e70f9 100644 --- a/pages/articles.js +++ b/pages/articles.js @@ -2,7 +2,7 @@ import Layout from '../components/Layout/Layout' import SEO from '../components/SEO/SEO' import Query from '../components/Query/query' import ARTICLES_QUERY from '../apollo/queries/articles/articles' -import { withApollo } from '../lib/apollo' +import withData from '../lib/apollo' const Articles = () => { return ( @@ -31,4 +31,4 @@ const Articles = () => { ) } -export default withApollo(Articles) +export default withData(Articles)