2

When I'm using an apollo provider with redux server side rendering,

https://github.com/reactjs/redux/blob/master/docs/recipes/ServerRendering.md

I get the following warning and it breaks the server side output

Warning: Failed context type: The context `client` is marked as required in `Apollo(Home)`, but its value is `undefined`.
    in Apollo(Home) (created by Connect(Apollo(Home)))
    in Connect(Apollo(Home)) (created by RouterContext)
    in RouterContext
    in Provider

However this renders fine client side.

app

window.webappStart = () => {
  const initialState = window.__PRELOADED_STATE__;
  const store = createStore(rootReducer, initialState);

  const client = new ApolloClient({
    networkInterface: createNetworkInterface({ uri: 'https://api.graph.cool/simple/v1/foo' }),
  });

  render(
    <ApolloProvider store={store} client={client}>
      <Router>{routes}</Router>
    </ApolloProvider>,
    document.querySelector(".js-content")
  );
};

Here's the boilerplate apollo code

import React from 'react';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';

// The data prop, which is provided by the wrapper below contains,
// a `loading` key while the query is in flight and posts when it is ready
function PostList({ data: { loading, posts } }) {
  if (loading) {
    return <div>Loading</div>;
  } else {
    return (
      <ul>
        {posts.map(post =>
          <li key={post.id}>
            {post.title} by {' '}
            {post.author.firstName} {post.author.lastName} {' '}
            ({post.votes} votes)
          </li>
        )}
      </ul>
    );
  }
}

// The `graphql` wrapper executes a GraphQL query and makes the results
// available on the `data` prop of the wrapped component (PostList here)
export default graphql(gql`
  query allPosts {
    posts {
      id
      title
      votes
      author {
        id
        firstName
        lastName
      }
    }
  }
`)(PostList);

1 Answer 1

1

The PostList component looks alright to me, as does the client-side initiation of your app.

If you're getting that error in your server logs, then I think you'll want to check your routing middleware to ensure you're passing the client to ApolloProvider before rendering your app.

I'm using Express v4.* and react-router v4. My setup looks like this:

import React from 'react'
import { renderToString } from 'react-dom/server'
import { match, RouterContext } from 'react-router'
import ApolloClient, { createNetworkInterface } from 'apollo-client'
import { ApolloProvider, renderToStringWithData } from 'react-apollo'
import routes from '../app/routes.js'
import { store } from 'app/store/index.js'

const Html = ({ title = 'App', content }) => (
  <html>
    <head>
      <title>{title}</title>
      <link href="/main.css" rel="stylesheet"/>
    </head>
    <body>
      <div id="root" dangerouslySetInnerHTML={{ __html: content }} />
      <script src='/index.js'/>
    </body>
  </html>
)

module.exports = (req, res) => {
  match(
    {
      location: req.originalUrl,
      routes,
    },
    (error, redirectLocation, renderProps) => {
      if (redirectLocation) {
        res.redirect(redirectLocation.pathname + redirectLocation.search)
      } else if (error) {
        console.error('ROUTER ERROR:', error)
        res.status(500)
      } else if (renderProps) {
        const client = new ApolloClient({
          ssrMode: true,
          networkInterface: createNetworkInterface({
            uri: 'http://localhost:8888/graphql',
          }),
        })

        /**
         * Make sure client is added here. Store is optional */
        const App = (
          <ApolloProvider client={client} store={store}>
            <RouterContext {...renderProps} />
          </ApolloProvider>
        )

        /**
         * Render and send response
         */
        renderToStringWithData(App).then(content => {
          const html = <Html content={content}/>

          res.status(200).send(`<!DOCTYPE html>\n${ renderToString(html) }`)
        }).catch((err) => console.log(`INITIAL RENDER (SSR) ERROR:`, err))
      } else {
        res.status(404).send('Not found')
      }
    }
  )
}

Sign up to request clarification or add additional context in comments.

1 Comment

I was trying to do this using Walmart's electrode framework electrode.io/docs/redux_router_engine.html and unfortunately couldn't figure out how to pass the client context to the rendering - getting a bit lost in their code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.