React-snap: material-ui / jss / dynamic imports

Created on 5 Jan 2018  路  7Comments  路  Source: stereobooster/react-snap

The default class names (.jss123) created by jss will be merged from different dynamic imports which breaks the design.

Setup:

Solution:
replace default name generator (http://cssinjs.org/js-api/?v=v9.5.0#generate-your-own-class-names) with a variation which uses random strings as part of the class name and wrap your app with this HOC.

import React, { Component } from 'react';
import JssProvider from 'react-jss/lib/JssProvider';
import { create } from 'jss';
import preset from 'jss-preset-default';
//import createGenerateClassName from 'material-ui/styles/createGenerateClassName';

import Layout from './components/pages/layout';

const createGenerateClassName = () => {
  let counter = 0
  return (rule, sheet) => `c${Math.random().toString(36).substring(2, 4) + Math.random().toString(36).substring(2, 4)}-${rule.key}-${counter++}`
}

const jss = create(preset());
// Custom Material-UI class name generator for better debug and performance.
jss.options.createGenerateClassName = createGenerateClassName;

function App() {
  return (
    <JssProvider jss={jss}>
      <Layout />
    </JssProvider>
  );
}

export default App;

please add this to the recipe section of your docs

Most helpful comment

Yes I was able to reproduce. Strange thing. I wonder how JSS works with SSR.

UPD: oh my

Once JS on the client is loaded, components initialized and your JSS styles are regenerated, it's a good time to remove server-side generated style tag in order to avoid side-effects

https://github.com/cssinjs/jss/blob/master/docs/ssr.md

render(<Button />, document.getElementById('app'), () => {
  // We don't need the static css any more once we have launched our application.
  const ssStyles = document.getElementById('server-side-styles')
  ssStyles.parentNode.removeChild(ssStyles)
})

Another fix

basically JSS doesn't support rehydration

import React from 'react';
import { hydrate, render } from 'react-dom';
import { loadComponents } from 'loadable-components';
import { getState } from 'loadable-components/snap';
import Index from './pages/index';

const app = <Index />;
const rootElement = document.getElementById('root');

loadComponents().then(() => {
  render(app, rootElement, () => {
    Array.from(document.querySelectorAll('[data-jss-snap]')).forEach(elem =>
      elem.parentNode.removeChild(elem),
    );
  });
});

window.snapSaveState = () => {
  Array.from(document.querySelectorAll('[data-jss]')).forEach(elem =>
    elem.setAttribute('data-jss-snap', ''),
  );
  return getState();
};

All 7 comments

I do not fully understand problem. I will need to investigate. Thanks for the tip

Yes I was able to reproduce. Strange thing. I wonder how JSS works with SSR.

UPD: oh my

Once JS on the client is loaded, components initialized and your JSS styles are regenerated, it's a good time to remove server-side generated style tag in order to avoid side-effects

https://github.com/cssinjs/jss/blob/master/docs/ssr.md

render(<Button />, document.getElementById('app'), () => {
  // We don't need the static css any more once we have launched our application.
  const ssStyles = document.getElementById('server-side-styles')
  ssStyles.parentNode.removeChild(ssStyles)
})

Another fix

basically JSS doesn't support rehydration

import React from 'react';
import { hydrate, render } from 'react-dom';
import { loadComponents } from 'loadable-components';
import { getState } from 'loadable-components/snap';
import Index from './pages/index';

const app = <Index />;
const rootElement = document.getElementById('root');

loadComponents().then(() => {
  render(app, rootElement, () => {
    Array.from(document.querySelectorAll('[data-jss-snap]')).forEach(elem =>
      elem.parentNode.removeChild(elem),
    );
  });
});

window.snapSaveState = () => {
  Array.from(document.querySelectorAll('[data-jss]')).forEach(elem =>
    elem.setAttribute('data-jss-snap', ''),
  );
  return getState();
};

Thank you @aheissenberger

function App() {

perfect, this solution was amazing , thanks a lot

thanks @aheissenberger and @stereobooster! Very helpful!

@aheissenberger I had today same problem with React.lazy() and JSS from Material-UI, without SSR. Your solution seems to be helpful. Do you think it is 1 to 1 related? I think so, but I'm not 100% sure.

Another solution

const generateClassName = createGenerateClassName({
  productionPrefix: navigator.userAgent === 'ReactSnap' ? 'snap' : 'jss',
});

<StylesProvider jss={jss} generateClassName={generateClassName}>
    <Layout/>
</StylesProvider>
Was this page helpful?
0 / 5 - 0 ratings

Related issues

stereobooster picture stereobooster  路  5Comments

wInevitable picture wInevitable  路  8Comments

lewisdonovan picture lewisdonovan  路  7Comments

jessevdp picture jessevdp  路  8Comments

stereobooster picture stereobooster  路  6Comments