reactjs/React.NET

Material UI

KennyLMQ opened this issue · 6 comments

This is a request for help.
I'm trying to use Material UI with React.NET (webpack).

During refresh, there is flickering and the styling loads only after a while.
Does this means there is some configuration I'm missing to ensure it's rendered on server?

Material UI have a guide regarding server side rendering, but I don't think I'm supposed to use that since I'm relying on React.NET for this.

Are there any resources that would help with these?

The docs are here around SSR for styling. The Material UI guide is relevant, because it describes the necessary steps to accomplish it for that library (albeit in node). Taking the styled-components integration as an example, it essentially does the same thing, but using C# to call the JS:

using System;

namespace React.RenderFunctions
{
	public class StyledComponentsFunctions : RenderFunctionsBase
	{
		public string RenderedStyles { get; private set; }

		public override void PreRender(Func<string, string> executeJs)
		{
			executeJs("var serverStyleSheet = new Styled.ServerStyleSheet();");
		}

		public override string WrapComponent(string componentToRender)
		{
			return ($"serverStyleSheet.collectStyles({componentToRender})");
		}

		public override void PostRender(Func<string, string> executeJs)
		{
			RenderedStyles = executeJs("serverStyleSheet.getStyleTags()");
		}
	}
}

It works, as long as:

  1. In the view(s), you call new StyledComponentsFunctions() and pass them to the renderFunctions. You also need to add the styles to the ViewBag, e.g.:
@{
    var styledComponentsFunctions = new StyledComponentsFunctions();
    var pageDataModel = Html.GetReactPageDataModel<HomePage, HomePageDataModel>(Model);
}

<!-- Render React app, passing page data -->
@Html.React("Components.App", (object)pageDataModel, renderFunctions: styledComponentsFunctions )

@{
    ViewBag.ServerStyles = styledComponentsFunctions.RenderedStyles;
}
  1. In the JS, you expose the Styled function:
global.Styled = { ServerStyleSheet };
  1. In your template(s), you reference the server-generated styles:
<!doctype html>
<head>
    @Html.Raw(ViewBag.ServerStyles)
    ...

ReactJS.NET calls the necessary functions (new Styled.ServerStyleSheet() etc. gathering your styles so we can output <style> tags). It's a little fragile, but that's really down to the nature of how the setup works.

To answer your question: using Material UI's SSR is not going to be particularly simple, because you will need to amend this library with the equivalent to the styled-components example.

One pointer: in Chrome/blink browsers you can easily disable JS in the dev tools. This lets you better test what is actually rendered server-side, and what depends on rehydration. Good luck!

I think this should be how it works, but again, not too sure.

import { ServerStyleSheets } from '@material-ui/styles'
global['MaterialUi'] = { ServerStyleSheets }

Add this to component

    React.useEffect(() => {
        if (typeof window !== 'undefined') {
            const jssStyles = document.querySelector('#jss-server-side');
            if (jssStyles) {
                jssStyles.parentElement?.removeChild(jssStyles);
            }
        }
    }, []);
  public class MaterialUIStyleFunctions : RenderFunctionsBase
    {
        /// <summary>
        /// HTML style tag containing the rendered styles
        /// </summary>
        public string RenderedStyles { get; private set; }

        public string Component { get; private set; }

        /// <summary>
        /// Implementation of PreRender
        /// </summary>
        /// <param name="executeJs"></param>
        public override void PreRender(Func<string, string> executeJs)
        {
            executeJs("const sheets = new MaterialUi.ServerStyleSheets()");
        }

        /// <summary>
        /// Implementation of WrapComponent
        /// </summary>
        /// <param name="componentToRender"></param>
        /// <returns></returns>
        public override string WrapComponent(string componentToRender)
        {
            Component = componentToRender;
            return Component;
        }

        /// <summary>
        /// Implementation of PostRender
        /// </summary>
        /// <param name="executeJs"></param>
        public override void PostRender(Func<string, string> executeJs)
        {
            executeJs($"var comp = {Component}");
            executeJs($"const html = ReactDOMServer.renderToString(sheets.collect(comp,))");
            RenderedStyles = $"<style type=\"text/css\" id=\"jss-server-side\">{executeJs("sheets.toString()")}</style>";
        }
    }

@KennyLMQ I'm trying to achieve a react.net and mui/emotion combo
Do you have any pointer on how too implement it?
Did you get the above suggestion to work?

@McDoit I gave up figuring how to do it. Sorry...
React.NET doesn't seems to be actively updated too, so I don't think I'll use it too.

ve suggestion to w

Hey @KennyLMQ and @McDoit have you tried again? did you achieve that? I haven't found any content related =/

Hey @tarumam, unfortunately, I gave up a while back and this project doesn't seem to be have been updated recently.

If you are not tied to using .NET, I would suggest using Next.js...