From 222ab374d53a206332b1ab51c7c43f6d5dc7a31e Mon Sep 17 00:00:00 2001 From: Wes Bos Date: Tue, 8 Aug 2017 22:09:05 -0400 Subject: yep --- lib/initApollo.js | 37 +++++++++++++++++++++++++ lib/withData.js | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 lib/initApollo.js create mode 100644 lib/withData.js (limited to 'lib') diff --git a/lib/initApollo.js b/lib/initApollo.js new file mode 100644 index 0000000..4ca3b05 --- /dev/null +++ b/lib/initApollo.js @@ -0,0 +1,37 @@ +import { ApolloClient, createNetworkInterface } from 'react-apollo' +import fetch from 'isomorphic-fetch' + +let apolloClient = null + +// Polyfill fetch() on the server (used by apollo-client) +if (!process.browser) { + global.fetch = fetch +} + +function create (initialState) { + return new ApolloClient({ + initialState, + ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once) + networkInterface: createNetworkInterface({ + uri: 'https://api.graph.cool/simple/v1/cj5xz8szs28930145gct82bdj', // Server URL (must be absolute) + opts: { // Additional fetch() options like `credentials` or `headers` + credentials: 'same-origin' + } + }) + }) +} + +export default function initApollo (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 (!process.browser) { + return create(initialState) + } + + // Reuse client on the client-side + if (!apolloClient) { + apolloClient = create(initialState) + } + + return apolloClient +} diff --git a/lib/withData.js b/lib/withData.js new file mode 100644 index 0000000..4720790 --- /dev/null +++ b/lib/withData.js @@ -0,0 +1,80 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { ApolloProvider, getDataFromTree } from 'react-apollo' +import Head from 'next/head' +import initApollo from './initApollo' + +// Gets the display name of a JSX component for dev tools +function getComponentDisplayName (Component) { + return Component.displayName || Component.name || 'Unknown' +} + +export default ComposedComponent => { + return class WithData extends React.Component { + static displayName = `WithData(${getComponentDisplayName(ComposedComponent)})` + static propTypes = { + serverState: PropTypes.object.isRequired + } + + static async getInitialProps (ctx) { + let serverState = {} + + // Evaluate the composed component's getInitialProps() + let composedInitialProps = {} + if (ComposedComponent.getInitialProps) { + composedInitialProps = await ComposedComponent.getInitialProps(ctx) + } + + // Run all GraphQL queries in the component tree + // and extract the resulting data + if (!process.browser) { + const apollo = initApollo() + // Provide the `url` prop data in case a GraphQL query uses it + const url = {query: ctx.query, pathname: ctx.pathname} + try { + // Run all GraphQL queries + await getDataFromTree( + + + + ) + } catch (error) { + // Prevent Apollo Client GraphQL errors from crashing SSR. + // Handle them in components via the data.error prop: + // http://dev.apollodata.com/react/api-queries.html#graphql-query-data-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 state = apollo.getInitialState() + + serverState = { + apollo: { + // Only include the Apollo data state + data: state.data + } + } + } + + return { + serverState, + ...composedInitialProps + } + } + + constructor (props) { + super(props) + this.apollo = initApollo(this.props.serverState) + } + + render () { + return ( + + + + ) + } + } +} -- cgit v1.3