styled-components: Potential memory leak when using the new ServerStyleSheet

Version

2.0.0

Today I picked up that memory usage was increasing continuously on one of our servers and I tracked it down to a commit which related to upgrading to the latest version of styled-components and editing some server-side code.

It seems that there may be a problem with the ServerStyleSheet as we were using getCSS before and this problem was not present.

Any help would be much appreciated!

The server-side rendering function which is called looks like this:

function serverSideRender(req, res, store) {
  const preloadedState = store.getState()
  const sheet = new ServerStyleSheet()
  const context = {}

  const markup = renderToString(
    <Provider store={store}>
      <StaticRouter location={req.url} context={context}>
        <StyleSheetManager sheet={sheet.instance}>
          <App />
        </StyleSheetManager>
      </StaticRouter>
    </Provider>,
  )

  const helmet = Helmet.renderStatic()

  res.status(200).send(`
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        ${helmet.title.toString()}
        ${helmet.meta.toString()}
        ${helmet.link.toString()}
        <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,700" rel="stylesheet">
        <link rel="stylesheet" href="/client.css" />
      </head>
      <body>
        <div id="root">${markup}</div>
        <script>window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}</script>
        <script src='/client.js'></script>
      </body>
    </html>
  `)
}

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 15 (6 by maintainers)

Most upvoted comments

@philpl @geelen you are correct. This particular issue happened to me, because I was using babel-plugin-styled-components only for the main repo, but not for the package that is shared between different repos. Adding the plugin to the shared package fixed my issue. Thanks for the help!

@geelen thanks for your comment - it seems we misunderstood the correct use of the StyleSheetManager and that was what caused the memory leak. That has been corrected now and everything seems to be working properly 👍

@philpl we did come other tests and the memory usage seems correlated with the number of requests. We have the problem if we use the StyleSheetManager or the sheet.collectStyles method so for now we reverted back using sheet.getStyleTags() and then manually put the style tags inside the template string.

Here the code snippets that we tested as they may could be useful as reference:

Case 1 - StyleSheetManager (memory leak)

function serverSideRender(req, res, store) {
  const preloadedState = store.getState()
  const sheet = new ServerStyleSheet()

  const markup = renderToString(
    <Provider store={store}>
      <StaticRouter location={req.url}>
        <StyleSheetManager sheet={sheet.instance}>
          <App />
        </StyleSheetManager>
      </StaticRouter>
    </Provider>,
  )

  res.status(200).send(`
    <!DOCTYPE html>
    <html lang="en">
      <head>
      </head>
      <body>
        <div id="root">${markup}</div>
      </body>
    </html>
 `)
}

Case 2 - sheet.collectStyles (memory leak)

function serverSideRender(req, res, store) {
  const preloadedState = store.getState()
  const sheet = new ServerStyleSheet()

  const AppWrapper = () => (
    <Provider store={store}>
      <StaticRouter location={req.url}>
        <App />
      </StaticRouter>
    </Provider>
  )

  const markup = renderToString(sheet.collectStyles(<AppWrapper />))

  res.status(200).send(`
    <!DOCTYPE html>
    <html lang="en">
      <head>
      </head>
      <body>
        <div id="root">${markup}</div>
      </body>
    </html>
 `)
}

Case 3 - sheet.getStyleTags (works just fine)

function serverSideRender(req, res, store) {
  const preloadedState = store.getState()
  const sheet = new ServerStyleSheet()
  const css = sheet.getStyleTags()

  const markup = renderToString(
    <Provider store={store}>
      <StaticRouter location={req.url}>
        <App />
      </StaticRouter>
    </Provider>,
  )

  res.status(200).send(`
    <!DOCTYPE html>
    <html lang="en">
      <head>
        ${css}
      </head>
      <body>
        <div id="root">${markup}</div>
      </body>
    </html>
 `)
}

We will do some tests next week to see whether the StylesheetProvider or the ServerStyleSheet is causing the issue. Our web app is receiving very minimal requests right now as we are still developing it - the total number of pages is around 10 at the moment. The memory usage is increasing at a constant rate regardless of the number of requests as far as I can tell.

I will feedback more information as we investigate further on Monday.