Getting checksum invalid warning when using apollo client isomorphically with react starter kit

223 views Asked by At

I'm trying to use the Apollo client and I am running into a few issues with dropping it into the react starter kit with redux. https://github.com/kriasoft/react-starter-kit/tree/feature/redux

Trying to use the techniques from here: http://dev.apollodata.com/react/server-side-rendering.html

But I get the error

warning.js:36Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
 (client) </div></header><div data-reactid="19">Lo
 (server) </div></header><div class="Home-root-2IM

Here's my implementation

// server.js
...
const component = (
  <App context={context}>
    <ApolloProvider client={context.client} store={context.store}>
      {route.component}
    </ApolloProvider>
  </App>
);


await getDataFromTree(component);

data.children = ReactDOM.renderToString(component);
data.style = [...css].join('');
data.scripts = [
  assets.vendor.js,
  assets.client.js,
];
data.state = context.store.getState();
if (assets[route.chunk]) {
  data.scripts.push(assets[route.chunk].js);
}

const html = ReactDOM.renderToStaticMarkup(<Html {...data} />);
res.status(route.status || 200);
res.send(`<!doctype html>${html}`);
...

And client side

// client.js
...
const component = (
  <App context={context}>
    <ApolloProvider client={context.client} store={context.store}>
      {route.component}
    </ApolloProvider>
  </App>
);

appInstance = ReactDOM.render(
 component,
  container,
  () => onRenderComplete(route, location),
);
...


// Home.js

class Home extends React.Component {
  static propTypes = {
    collections: PropTypes.arrayOf(PropTypes.shape({
      id: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired,
      subtitle: PropTypes.string.isRequired,
      photo: PropTypes.string,
    })).isRequired,
  };

  render() {
    const props = this.props;
    const { loading, allCollections } = props.data;

    if (loading) {
      return <div>Loading</div>;
    } else {
      return (
        <div className={s.root}>
          <div className={s.container}>
            <h1 className={s.title}>Collections</h1>
            <ul>
              {allCollections.map((collection) =>
                <li key={collection.id}>
                  <h3>{collection.title}</h3>
                  <img src={collection.photo} width="200"/>
                </li>
              )}
            </ul>
          </div>
        </div>
      );
    }

  }
}

Home.propTypes = {
  data: PropTypes.shape({
    loading: PropTypes.bool.isRequired,
    allCollections: PropTypes.array,
  }).isRequired,
};


const HomeWithStyles =  withStyles(s)(Home);
const HomeWithData = graphql(getQuery)(HomeWithStyles);
export default connect()(HomeWithData);



// App.js
import React, { Children, PropTypes } from 'react';

const ContextType = {
  // Enables critical path CSS rendering
  // https://github.com/kriasoft/isomorphic-style-loader
  insertCss: PropTypes.func.isRequired,
  // Integrate Redux
  // http://redux.js.org/docs/basics/UsageWithReact.html
  store: PropTypes.shape({
    subscribe: PropTypes.func.isRequired,
    dispatch: PropTypes.func.isRequired,
    getState: PropTypes.func.isRequired,
  }).isRequired,
  client: PropTypes.object.isRequired,
};

/**
 * The top-level React component setting context (global) variables
 * that can be accessed from all the child components.
 *
 * https://facebook.github.io/react/docs/context.html
 *
 * Usage example:
 *
 *   const context = {
 *     history: createBrowserHistory(),
 *     store: createStore(),
 *   };
 *
 *   ReactDOM.render(
 *     <App context={context}>
 *       <Layout>
 *         <LandingPage />
 *       </Layout>
 *     </App>,
 *     container,
 *   );
 */
class App extends React.PureComponent {

  static propTypes = {
    context: PropTypes.shape(ContextType).isRequired,
    children: PropTypes.element.isRequired,
  };

  static childContextTypes = ContextType;

  getChildContext() {
    return this.props.context;
  }

  render() {
    // NOTE: If you need to add or modify header, footer etc. of the app,
    // please do that inside the Layout component.
    return Children.only(this.props.children);
  }

}

export default App;
1

There are 1 answers

0
MonkeyBonkey On

So the answer was that the initial state was not set for the apollo client data

// client.js 

let apolloOptions = {
  ssrMode: false,
  initialState: { apollo: { data: window.APP_STATE.apollo.data } }, // NOT __APOLLO_STATE__ as set in some of the examples online 
  ssrForceFetchDelay: 500,
  networkInterface: createNetworkInterface({
    uri: window.APP_STATE.runtime.graphUri,
  }),
  opts: {
    credentials: 'same-origin',
  },
};
const client = new ApolloClient(apolloOptions);

And on the server

// server.js

...
// data.state gets rendered by the server to client as APP_STATE
data.state = context.store.getState();
data.state.apollo = client.store ? client.store.getState().apollo : null;
...