Khan / aphrodite

Framework-agnostic CSS-in-JS with support for server-side rendering, browser prefixing, and minimum CSS generation

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Aphrodite / GraphQL / SSR - cannot automatically buffer without a document

14850842 opened this issue · comments

I have implemented a Graphql Server Side Rendered (razzle) react app. We get an intermittent error
cannot automatically buffer without a document when loading the app.
It seems to be an issue when apollow gets the data from the tree and tries to compile the styles at that point.

A suggested method was to call the following:

StyleSheetTestUtils.suppressStyleInjection();
await getDataFromTree(WrappedApp);
StyleSheetTestUtils.clearBufferAndResumeStyleInjection();

But running this gives the intermittent error. Have tried other various technique but have run out of options. Server get request shown below.

server.get("/*", async (req, res, next) => {
  try {
    const client = new ApolloClient({
      ssrMode: true,
      link: createHttpLink({
        uri: process.env.RAZZLE_GQL_HOST,
        fetch: fetch
      }),
      cache: new InMemoryCache()
    });

    const context = {};
    const WrappedApp = (
      <ApolloProvider client={client}>
          <StaticRouter location={req.url} context={context}>
            <App />
          </StaticRouter>
      </ApolloProvider>
    );

    await getDataFromTree(WrappedApp);

    let metaHeader = Helmet.renderStatic();

    const { html, css } = StyleSheetServer.renderStatic(() =>
      renderToStaticMarkup(WrappedApp)
    );

    if (context.url) {
      return res.redirect(context.url);
    } else {
      return res.status(200).send(
        `<!doctype html>
            <html ${metaHeader.htmlAttributes.toString()}>
              <head>
                  <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
                  <meta charset="utf-8">
                  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
                  <style data-aphrodite>${css.content}</style>
                  ${
                    assets.client.css
                      ? `<link rel="stylesheet" href="${assets.client.css}">`
                      : ""
                  }

                  ${
                    process.env.NODE_ENV === "production"
                      ? `<script src="${assets.client.js}" defer></script>`
                      : `<script src="${
                          assets.client.js
                        }" defer crossorigin></script>`
                  }

                  ${metaHeader.title.toString()}
              </head>
              <body>
                  <div id="root">${html}</div>
              </body>
              <script>
                window.__APHRODITE_STATE__=${JSON.stringify(
                  css.renderedClassNames
                )}
                window.__APOLLO_STATE__=${JSON.stringify(
                  client.cache.extract()
                )};
              </script>
          </html>`
      );
    }
  } catch (err) {
    return next(err);
  }
});

Same exact issue here. Any luck?

Same issue here. This essentially makes aphrodite unusable for my current project, which is sad. I'll dig more into some render calls to see what we can do about it.

Same problem at Airbnb. We we're going to implement this at the interface level, swapping out a noop interface for the Aphrodite interface, but if we solve this within aphordite we could use that potentially.

I've got a barebones version working that I will PR soon @adamrneary. I just need to update some documentation for the new methods.

EDIT: I haven't solved the hydration problem in terms of server-side rendering and classes depending upon potential state gained from tree walking with e.g. Apollo, but it does give leeway to using Aphrodite later on in your server-side server logic.

@adamrneary @zaklampert @14850842 This PR aims to solve the basic issue. A better solution would presumably be to extend more of the existing interface to respect server-side rendering, but this is a immediate solution to the problem, assuming you don't need the data from your GraphQL queries to generate classes.