import React from 'react' import PropTypes from 'prop-types' import { ApolloProvider, getDataFromTree } from 'react-apollo' import initApollo from './initApollo' import initRedux from './initRedux' // 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() const redux = initRedux(apollo) // Provide the `url` prop data in case a graphql query uses it const url = {query: ctx.query, pathname: ctx.pathname} // Run all graphql queries const app = ( // No need to use the Redux Provider // because Apollo sets up the store for us ) await getDataFromTree(app) // Extract query data from the store const state = redux.getState() // No need to include other initial Redux state because when it // initialises on the client-side it'll create it again anyway serverState = { apollo: { // Make sure to only include Apollo's data state data: state.apollo.data } } } return { serverState, ...composedInitialProps } } constructor (props) { super(props) this.apollo = initApollo() this.redux = initRedux(this.apollo, this.props.serverState) } render () { return ( // No need to use the Redux Provider // because Apollo sets up the store for us ) } } }