3

I have an express server that uses Server Side Rendering (SSR) to render a react app.

I want to use the materialize-css package together with my react app.

I have imported the materialize-css/dist/css/materialize.min.css and materialize-css/dist/js/materialize.min into my App.js.

Before I used express to render my app server side it worked fine, styling and modals from materialize was loaded correctly.

Now when I try to render my app using express, I get following error:

[0] ReferenceError: window is not defined
[0]     at Object.<anonymous> (/Users/user/Sites/task_keeper/node_modules/materialize-css/dist/js/materialize.min.js:6:1135)
[0]     at Module._compile (internal/modules/cjs/loader.js:778:30)
[0]     at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
[0]     at Module.load (internal/modules/cjs/loader.js:653:32)
[0]     at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
[0]     at Function.Module._load (internal/modules/cjs/loader.js:585:3)
[0]     at Module.require (internal/modules/cjs/loader.js:692:17)
[0]     at require (internal/modules/cjs/helpers.js:25:18)
[0]     at eval (webpack:///external_%22materialize-css/dist/js/materialize.min%22?:1:18)

This is my render middleware:

const renderMiddleware = () => (req, res) => {
  let html = req.html;
  const htmlContent = ReactDOMServer.renderToString(<App />);
  const htmlReplacements = {
    HTML_CONTENT: htmlContent,
  };

  Object.keys(htmlReplacements).forEach(key => {
    const value = htmlReplacements[key];
    html = html.replace(
      new RegExp('__' + escapeStringRegexp(key) + '__', 'g'),
      value
    );
  });

  res.send(html);
};

And then I use hydrate in index.js,

ReactDOM.hydrate(<App />, document.getElementById('root'));.

What is the correct way to use materialize-css in a setup like this?

  • window should be the parent object of document, server-sided it's not existing though as it's related to the browers and could be a virtual container. If you work with (I)frames you can probably remark similar problems. – David Nov 22 '19 at 13:34
  • @David Thank you for the reply. I know that window and document is not available server side, so I'm thinking about loading the js after the app has been rendered server side. Do you think that's possible? –  Nov 22 '19 at 13:40
  • It doesn't change much concerning `window` and `document` but that would probably enable you to render without errors first. So yes, sounds like a reasonable and good solution. – David Nov 22 '19 at 13:42
  • another question is if you can emulate window and document server sided, but if it's not required that would be better probably – David Nov 22 '19 at 13:44
  • That would be cool. –  Nov 22 '19 at 13:45
  • With drupal you're using PHP, so you could have a look how the PHP-DOM library is treating `window` and `document` - if existing at all – David Nov 22 '19 at 13:47
  • 1
    I found this post which seems interesting, https://stackoverflow.com/questions/32126003/node-js-document-is-not-defined. –  Nov 22 '19 at 13:48
  • I'm a bit astonished that `document` never exists server sided but the DOM-Library has one: https://www.php.net/manual/en/class.domdocument.php but it seems it's just related to a text-document in HTML or XML. – David Nov 22 '19 at 13:53
  • Have a look here, in node.js you could emulate it withe help of the global keyword: https://stackoverflow.com/a/14452632/1019850 – David Nov 22 '19 at 14:10

1 Answers1

0

You can solve this by loading the script with a CDN, like this:

<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>

This way the script is injected into the window variable, so you can use window.M.AutoInit(), for instance.

So in the component where you use the modals, autoinit them in a useEffect callback:

useEffect(() => {
  window.M.AutoInit();
}, []);