Apollo decorator refactor.
This commit is contained in:
parent
4b9f461c40
commit
8d3262500d
8
app/helpers/get-display-name.js
Normal file
8
app/helpers/get-display-name.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Gets the display name of a JSX component for dev tools.
|
||||||
|
* @param {Object | Function} component - A JSX component.
|
||||||
|
* @returns {String} The component display name.
|
||||||
|
*/
|
||||||
|
export default function getDisplayName({ displayName, name }) {
|
||||||
|
return displayName ? displayName : name && name !== '' ? name : 'Unknown'
|
||||||
|
}
|
||||||
37
app/helpers/init-apollo-client.js
Normal file
37
app/helpers/init-apollo-client.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { ApolloClient } from 'react-apollo'
|
||||||
|
import { createNetworkInterface } from 'apollo-upload-client'
|
||||||
|
import 'isomorphic-fetch'
|
||||||
|
|
||||||
|
// Used in the browser to share a single Apollo Client instance between
|
||||||
|
// decorated components.
|
||||||
|
let apolloClient = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Apollo Client instance.
|
||||||
|
* @param {Object} [initialState] - Apollo client Redux store initial state.
|
||||||
|
* @returns {Object} Apollo Client instance.
|
||||||
|
*/
|
||||||
|
const createApolloClient = initialState =>
|
||||||
|
new ApolloClient({
|
||||||
|
initialState,
|
||||||
|
ssrMode: !process.browser,
|
||||||
|
networkInterface: createNetworkInterface({
|
||||||
|
uri: process.env.API_URI
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets or creates the Apollo Client instance.
|
||||||
|
* @param {Object} [initialState] - Apollo client Redux store initial state.
|
||||||
|
* @returns {Object} Apollo Client instance.
|
||||||
|
*/
|
||||||
|
export default function initApolloClient(initialState) {
|
||||||
|
// Create a new client every server-side request so that data isn't shared
|
||||||
|
// between connections.
|
||||||
|
if (!process.browser) return createApolloClient(initialState)
|
||||||
|
|
||||||
|
// Reuse client on the client-side.
|
||||||
|
if (!apolloClient) apolloClient = createApolloClient(initialState)
|
||||||
|
|
||||||
|
return apolloClient
|
||||||
|
}
|
||||||
@ -1,73 +1,92 @@
|
|||||||
import 'isomorphic-fetch'
|
import { Component } from 'react'
|
||||||
import React from 'react'
|
import { ApolloProvider, getDataFromTree } from 'react-apollo'
|
||||||
import { ApolloClient, ApolloProvider, getDataFromTree } from 'react-apollo'
|
import Head from 'next/head'
|
||||||
import { createNetworkInterface } from 'apollo-upload-client'
|
import initApolloClient from './init-apollo-client'
|
||||||
|
import getDisplayName from './get-display-name'
|
||||||
|
|
||||||
const ssrMode = !process.browser
|
export default ComposedComponent => {
|
||||||
let apolloClient = null
|
return class WithData extends Component {
|
||||||
|
static displayName = `WithData(${getDisplayName(ComposedComponent)})`
|
||||||
|
|
||||||
function initClient(headers, initialState) {
|
/**
|
||||||
return new ApolloClient({
|
* Gets the initial props for a Next.js page component.
|
||||||
initialState,
|
* Executes on the server for the initial page load. Executes on the client
|
||||||
ssrMode,
|
* when navigating to a different route via the Link component or using the
|
||||||
networkInterface: createNetworkInterface({
|
* routing APIs. For either environment the initial props returned must be
|
||||||
uri: process.env.API_URI
|
* serializable to JSON.
|
||||||
})
|
* @see https://github.com/zeit/next.js/issues/978
|
||||||
})
|
* @see https://github.com/zeit/next.js/#fetching-data-and-component-lifecycle
|
||||||
}
|
* @param {Object} context
|
||||||
|
* @param {String} context.pathname - Path section of the page URL.
|
||||||
function getClient(headers, initialState = {}) {
|
* @param {Object} context.query - Query string section of the page URL parsed as an object.
|
||||||
if (ssrMode) return initClient(headers, initialState)
|
* @param {Object} context.req - HTTP request (server only).
|
||||||
if (!apolloClient) apolloClient = initClient(headers, initialState)
|
* @param {Object} context.res - HTTP response (server only).
|
||||||
return apolloClient
|
* @param {Object} context.jsonPageRes - Fetch Response (client only).
|
||||||
}
|
* @param {Object} context.err - Error encountered during the rendering, if any.
|
||||||
|
* @returns {Promise} Page component props.
|
||||||
export default Component =>
|
*/
|
||||||
class extends React.Component {
|
static async getInitialProps(context) {
|
||||||
static async getInitialProps(ctx) {
|
const initialProps = {
|
||||||
const headers = ctx.req ? ctx.req.headers : {}
|
composedComponentProps: {
|
||||||
const client = getClient(headers)
|
url: {
|
||||||
|
query: context.query,
|
||||||
const props = {
|
pathname: context.pathname
|
||||||
url: {
|
|
||||||
query: ctx.query,
|
|
||||||
pathname: ctx.pathname
|
|
||||||
},
|
|
||||||
...(await (Component.getInitialProps
|
|
||||||
? Component.getInitialProps(ctx)
|
|
||||||
: {}))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ssrMode) {
|
|
||||||
const app = (
|
|
||||||
<ApolloProvider client={client}>
|
|
||||||
<Component {...props} />
|
|
||||||
</ApolloProvider>
|
|
||||||
)
|
|
||||||
await getDataFromTree(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
initialState: {
|
|
||||||
apollo: {
|
|
||||||
data: client.getInitialState().data
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
headers,
|
|
||||||
...props
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the page component has initial props, merge them in.
|
||||||
|
if (ComposedComponent.getInitialProps) {
|
||||||
|
Object.assign(
|
||||||
|
initialProps.composedComponentProps,
|
||||||
|
await ComposedComponent.getInitialProps(context)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.browser) {
|
||||||
|
const apolloClient = initApolloClient()
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Recurse the component tree and prefetch all Apollo data queries to
|
||||||
|
// populate the Apollo Client Redux store. This allows an instant
|
||||||
|
// server side render.
|
||||||
|
// See: http://dev.apollodata.com/react/server-side-rendering.html#getDataFromTree
|
||||||
|
await getDataFromTree(
|
||||||
|
<ApolloProvider client={apolloClient}>
|
||||||
|
<ComposedComponent {...initialProps.composedComponentProps} />
|
||||||
|
</ApolloProvider>
|
||||||
|
)
|
||||||
|
} 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forget Head items found during the getDataFromTree render to prevent
|
||||||
|
// duplicates in the real render.
|
||||||
|
Head.rewind()
|
||||||
|
|
||||||
|
// Set Apollo Client initial state so the client can adopt data fetched
|
||||||
|
// on the server.
|
||||||
|
initialProps.initialState = {
|
||||||
|
apollo: apolloClient.getInitialState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return initialProps
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.client = getClient(this.props.headers, this.props.initialState)
|
this.apolloClient = initApolloClient(this.props.initialState)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={this.client}>
|
<ApolloProvider client={this.apolloClient}>
|
||||||
<Component {...this.props} />
|
<ComposedComponent {...this.props.composedComponentProps} />
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user