问题描述:

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);

网友答案:

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')
      }
    }
  )
}
相关阅读:
Top