gatsby: Material-UI and Gatsby v2 - hot reload does not work for styles
This is basically a re-post from the Material-UI issue in hopes that someone can help me.
I’m using Material-UI v3.1.0 with Gatsby v2.0.0 following the official example. Gatsby v2 changed how layout works, specifically the changes after gatsby@2.0.0-beta.59 caused many headaches for me. I managed to get my production build to work perfectly, but development is a different story. Styles work, but hot reload does not, which makes dev story pretty bad. Having to refresh the page after each style change sucks big time.
I don’t have time to debug it ATM, going for a short break, but decided to post the issue. Maybe it magically resolves when I come back a week later 😃
My use case is a bit different than the example. I use Gatsby’s new wrapPageElement to wrap page with my custom Layout. Inside that layout, I use withRoot from the example. This way, JssProvider and MuiThemeProvider are created only once, not each time a different page is rendered. I suspect this might be the reason why hot reload does not work. Then again, it could be related to babel@7 problems with RHL. Note that in the example, hot reload works, but it’s not using the wrapPageElement api. Would be cool if there was some example using it.
Furthermore, JssProvider is also created inside replaceRenderer method. So now I have two JSS providers on SSR: one in replaceRenderer and one in withRoot. This is bad, right? I already created an issue on Gatsby’s issue tracker, but got nowhere. I guess there’s not so many people living on the edge 😃
Anyway, here’s my code:
(note that gatsby-browser.js is pretty much the same as gatsby-ssr.js without the replaceRenderer)
const React = require('react')
const { renderToString } = require('react-dom/server')
const { JssProvider } = require('react-jss')
const postcss = require('postcss')
const autoprefixer = require('autoprefixer')
const csswring = require('csswring')
const { AdyenProvider } = require('providers/adyen')
const { CartProvider } = require('providers/cart')
const getPageContext = require('utils/getPageContext').default
const Layout = require('components/layout/Layout').default
const prefixer = postcss([autoprefixer])
const minifier = postcss([csswring])
exports.wrapRootElement = ({ element }) => {
return (
<AdyenProvider>
<CartProvider>
{element}
</CartProvider>
</AdyenProvider>
)
}
exports.wrapPageElement = ({ element, props }) => {
return (
<Layout {...props}>{element}</Layout>
)
}
exports.replaceRenderer = ({ bodyComponent, replaceBodyHTMLString, setHeadComponents }) => {
const muiPageContext = getPageContext()
const bodyHTML = renderToString(
<JssProvider registry={muiPageContext.sheetsRegistry}>
{bodyComponent}
</JssProvider>
)
replaceBodyHTMLString(bodyHTML)
// extract and minify styles
const css = muiPageContext.sheetsRegistry.toString()
const options = { from: undefined }
const prefixed = prefixer.process(css, options)
const minified = minifier.process(prefixed.css, options)
setHeadComponents([
<style
type={'text/css'}
id={'server-side-jss'}
key={'server-side-jss'}
dangerouslySetInnerHTML={{ __html: minified.css }}
/>,
])
}
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 8
- Comments: 48 (34 by maintainers)
Commits related to this issue
- Switch to Mui 4@next to prevent hot reloading issues See https://github.com/mui-org/material-ui/tree/next/examples/gatsby-next and https://github.com/gatsbyjs/gatsby/issues/8237 — committed to livejam/website-old by skorfmann 5 years ago
- Allow hot reloading outside of page content... https://github.com/gatsbyjs/gatsby/issues/8237#issuecomment-422213443 Though according to someone on discord, hot reloading should have been working out... — committed to amcsi/szeremi by amcsi 5 years ago
This seriously should be solved.
I have started working on the problem. You can expect a first-class support of Material-UI v4 with Gatsby. We gonna use https://github.com/hupe1980/gatsby-plugin-material-ui for that.
I fiddled around, thats what I found:
wrapRootElementand theMuiThemeProviderseem to be unrelated to hot module reloading not working. PlainwithStyles()without theme do not trigger a HMR as well.ruleCountercounts up properly (Related: https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/styles/createGenerateClassName.js)gatsby-plugin-jssand it’s example in the gatsby repo. Here HMR is broken as well, but the behavior is mirrored: New class names are set for the components, the new styles are not injected in the DOM.sheetManageris still used to get the class names for the component. A newsheetManagerexists insheetsManagerwith the new styles applied, but that one is not used. Thats why nothing changes, the css is there but the class names are not updated. Probably this line: https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/styles/withStyles.js#L195stylesCreatorSavedstaying the same value all the timecomponentDidUpdateis not called at all, the HoC is actually reconstructed and remounted every time the styles get updated. I guess here lies the problem.I hope this helps @HriBB 😃
I think that we can close the issue. react-hot-loader is supported with the higher-order and hook APIs:
This is using Material-UI v4.0.0-alpha.1 and this example: https://github.com/mui-org/material-ui/tree/next/examples/gatsby-next.
Is SSR with Gatsby and MUI currently broken? I am not able to get things working and the message in the example says it’s not ready yet
Is there any workaround?
@oliviertassinari Any ideas?
FWIW, I don’t remember this being an issue when I filed #7716 and #7568 (there was another issue, related to not being able to track styles when building more than one page).
I’m sure @oliviertassinari’s method works, but you then lose out on the ability to have smooth transitions between pages with shared layouts…
Which is what
wrapPageElementandwrapRootElementwere intended to resolve.Looking through the current open issues, it seems that HMR is responsible for quite a few. Maybe it’s worth looking at how Next.js migrated away from
react-hot-loader, since it seems like it has caused on-going issues.I noticed in the changelog that it used to have issues with stateless components, so I tried converting all my stateless components to
React.Componentbut that didn’t resolve this issue.My biggest complaint with Gatsby is that I can have something looking amazing in the
developmode, but it always has countless issues to debug when I try tobuild.This issue is still there and can be reproduced by the latest material example of gatsby. I can provide a codesandbox url if you need, since it requires some work.
It’s fixed in https://github.com/mui-org/material-ui/pull/16195 and will be soon released in Material-UI v4.1.1.
Just tested a production build and it worked successfully! no more first paint issues
Note per https://www.gatsbyjs.org/packages/gatsby-plugin-offline/#remove:
It’s now suggested to add the
gatsby-plugin-remove-serviceworkerplugin to ensure that the service worker is removed completely. (Otherwise the service worker still tries to take over and jumbles all the styles.) I previously had to force-clear the site’s app data in Chrome, which I obviously couldn’t expect users of a public site to do.I managed to get hot reloading to work by using
export default hot(module)(PageOrTemplate).Had to add this to all my pages and templates. Hacky, but at least I can work normally …
Temporal workaround from react-hot-loader issue:
If you use the gatsby example from the MUI project: You need to paste
on top of the function definition of your root layout i.e. the top layout of the plugin.
It’s a recent regression with an update of react-hot-loader.
this is linked from https://github.com/mui-org/material-ui/blob/master/examples/gatsby/gatsby-ssr.js so I thought I’d jump back in, though I originally commented on hot reloading, i’ve progressed on the project and got to production issues. Here’s what worked for me.
withWidth()<Hidden implementation="css" />gatsby-plugin-offlineThe last bullet is the new suggestion on the thread. Without it, the initial page would look as expected from html and then when the js loaded the styles got weird.