React Server side rendering with Express - warning on the client checksum & Styling - javascript

I am new to React server side rendering , working on a small demo with React , Redux , React router , Material UI. The problem I am facing is the below warning. Unsure how the isomorphic styling and assets works with webpack.
I understood the pipeline how server side rendering work , please correct me if it is wrong.
Using renderToString the React component get resolved to the HTML.
Once the HTML renders on the client side , all the events , styling gets attached meaning react try to render the component on the client side again , if the component is already created it will create it again.
If the component is already created or NOT is derived based on the Checksum.
Issue reported in GIT
https://github.com/callemall/material-ui/issues/4466
Code
https://github.com/sabha/React-Redux-Router-MaterialUI-ServerSideRendering
'warning.js:44Warning: React attempted to reuse markup in a container
but the checksum was invalid. This generally means that you are using
server rendering and the markup generated on the server was not what
the client was expecting. React injected new markup to compensate
which works but you have lost many of the benefits of server
rendering. Instead, figure out why the markup being generated is
different on the client or server: (client)
0;text-align:center;mui-prepared:;-webki (server)
0;text-align:center;-webkit-user-select:'

In this Razzle Material UI Styled Example project I am setting the user agent this way:
server.js:
renderToString(<Application userAgent={request.headers['user-agent']} />)
client.js:
hydrate(<Application userAgent={navigator.userAgent} />, document.getElementById('root'))
Main.js:
class Main extends Component {
constructor(properties, context) {
super(properties, context)
this.muiTheme = getMuiTheme({
userAgent: properties.userAgent
})
}
render() {
return (
<MuiThemeProvider muiTheme={this.muiTheme}></MuiThemeProvider>
)
}
}
It works well and I think it is correct too.

Material-UI, with its inline-style approach to styling elements, has this gotcha with respect to server-side rendering:
On the client, MaterialUI rendering only adds inline styles specific to the browser that is running the app. But on the server, you aren't in a browser and so how to know which specific style rules to use so that the rendered HTML matches the client-rendered HTML and avoids the React warning?
They have documentation on how to solve the issue. Essentially it means ensuring you set the user agent string (captured from the HTTP Request Header) before doing your server side rendering.
This is from their serverside rendering documentation:
import getMuiTheme from 'material-ui/getMuiTheme';
import MuiThemeProvider from 'material-ui/MuiThemeProvider';
import {green100, green500, green700} from 'material-ui/styles/colors';
const muiTheme = getMuiTheme({
palette: {
primary1Color: green500,
primary2Color: green700,
primary3Color: green100,
},
}, {
avatar: {
borderColor: null,
},
userAgent: req.headers['user-agent'],
});
class Main extends React.Component {
render() {
return (
<MuiThemeProvider muiTheme={muiTheme}>
<div>Hello world</div>
</MuiThemeProvider>
);
}
}

(client) 0;text-align:center;mui-prepared:;-webki
(server) 0;text-align:center;-webkit-user-select:
You will notice that the difference between the two is extra mui-prepared that is not present in the server render. There is a note about this at the bottom of the server rendering guide in the docs.
In order to make sure our style transformations are only applied once, we add an additional property to each style when process.env.NODE_ENV !== 'production'.
It looks like you have process.env.NODE_ENV=production only on the server (this was the case for me).

Related

You're importing a component that needs useState. It only works in a Client Component, but none of its parents are marked with "use client"

The simple below component for handling files uploading is throwing the following error in Next.js's app directory:
You're importing a component that needs useState. It only works in a Client Component, but none of its parents are marked with "use client", so they're Server Components by default.
import { useState } from "react";
export default function FileUploader() {
const [files, setFiles] = useState([]);
return (
<div>
Hello World
</div>
);
}
In the app directory, by default, Next.js uses Server Components, where the JSX gets compiled to "pure HTML" and sent to the browser. Like any traditional Backend with a templating engine, such as Express with EJS, and Laravel with Blade. This is for better performance, as you can read on the doc:
Server Components allow developers to better leverage server infrastructure. For example, large dependencies that previously would impact the JavaScript bundle size on the client can instead remain entirely on the server, leading to improved performance.
And a Server Component shouldn't contain front-end-specific code, for example, hooks such as useState or useEffect. If you need that, your component or one of its parents should have "use client" at the top, to make it a Client Component:
"use client"; // this is a client component ๐Ÿ‘ˆ๐Ÿฝ
import { useState } from "react";
export default function FileUploader() {
const [files, setFiles] = useState([]);
return (
<div>
Hello World
</div>
);
}

too much render with react markdown

I am using React Markdown (https://www.npmjs.com/package/react-markdown) to render markdown content in my NextJS project.
When I refresh I have two "toto" & "titi" in my terminal... It is normal or what's wrong with this code?
import Head from 'next/head';
import ReactMarkdown from 'react-markdown';
function Section ({ data }) {
const content = JSON.parse(data.markdown);
const {
title,
sortContent
} = data;
console.log('toto');
return (
<>
<main>
<h1>{title}</h1>
<h1>{sortContent}</h1>
<ReactMarkdown source={content.default} escapeHtml={false} />
</main>
</>
)
}
export async function getServerSideProps (context) {
const json = await import('../../content/article1/data.json');
const content = await import('../../content/fr/article1/content.md');
console.log('titi');
return {
props: {
data: {
title: json.title_content,
sortContent: json.short_content,
markdown: JSON.stringify(content)
}
}
}
}
export default Section
It's part of Reacts development tooling, StrictMode. It is expected and only applies in development mode. You can remove the StrictMode to see it only render the expected number of times, but obviously you lose some development tooling. This tooling can warn you about certain unsafe or unwise practices you might want to avoid such as using legacy APIs.
More details here:
Reactjs Docs
A blog with a good overview
If this is truly the only code you have, then it looks like it's normal. You may have other code that uses these components and that's why in shows twice. But based off the code you have right there, there's no bug.
This is a known side-effect of using React.StrictMode, only in debug mode. You can read more about this here.
Strict mode canโ€™t automatically detect side effects for you, but it
can help you spot them by making them a little more deterministic.
This is done by intentionally double-invoking the following functions:
Class component constructor, render, and shouldComponentUpdate methods
Class component static getDerivedStateFromProps method
Function component bodies
State updater functions (the first argument to setState) Functions passed to useState, useMemo, or useReducer

React Martial UI component overwritten/conflicts when new component is loaded

I am talking about a big web app that has jquery and bootstrap stuff init.
Part of rewriting this giant app using react and material UI, we are writing component by component. Things work fine in general as we make progress toward making this a react app soon.
Our problem:
When a new react component is loaded into the page, the existing (already loaded) react components will lose some or all styles.
We checked the new component load new style (classes) which are matching the names of existing classes for other already loaded components.
Ex:
As you can see, jss1, jss2, ... MuiCardHeader, MuiGrid, ... were also the names for the previously loaded components and now they are overwritten for the newly loaded component.
Packages.json:
webpack config:
Some component code: we are using make style and in some cases withstyle
Tried too much stuff. But nothing seems to work. On initial load, the map has all the correct material-ui stuff but as soon as I click on a marker and the popup component loads in. Now the map is messed up as some styles are overwritten.
How can we make it so that each component styles are named unique and never conflicts with other stuff?
I checked MUI docs and GitHub issues about kinda similar issue but nothing is working for us.
Any thoughts?
If I add:
import { StylesProvider, createGenerateClassName } from '#material-ui/core/styles';
const generateClassName1 = createGenerateClassName({
seed: 'App2',
});
my custom classes ( like root, mydivstyle) will have the prefix like app2-jss1-root, app2-jss2-mydivstyle, ...
but muiCard, MuiCardHeader, ... still being overwritten.
So after a few trial and error. I was able to solve this.
Basically we don't have a component tree path where you can could add a single class generator because of our app structure at this stage. We will soon have one.
The solution was to wrap every component like.
import { StylesProvider, createGenerateClassName } from '#material-ui/core/styles';
const generateClassName = createGenerateClassName({
seed: 'anyprefix',
});
class ActivityContainer extends Component {
render() {
return (
<StylesProvider generateClassName={generateClassName}>
<componenet/>
</StylesProvider>
);
};
}
I had to do this on the container level, this way any component loaded in that container will inherit this prefix.
No more conflicting class names between components and can load them dynamically.
Not a great way but no other option at least in our case. It will leave more classes on the DOM.

Getting error when trying to use ReactJS.Net server side rendering with EPiServer

I am trying to set up server side rendering using ReactJS.Net in an EPiServer project. In order to do that I am using the provided ReactJS.Net Html extention to do my rendering, however, when I run my site
I get an error saying: "ReactJS.NET has not been initialised correctly. Please ensure you have called services.AddReact() and app.UseReact() in your Startup.cs file."
If I swap from trying to use the server rendering extension to trying to render normally with React
I get a different error: "TinyIoCResolutionException: Unable to resolve type: React.Web.BabelHandler".
I think the root issue is that there is some difference in how an EPiServer project initializes things at start up vs a vanilla .Net MVC project, but I am unsure of what the difference is or how to work around it.
I am working with the React.Web.Mvc4 v4.0.0 nuget package, Episerver 11, and .Net Framework 4.6.2. I took the same component I'm trying to render and pasted it into a vanilla .Net MVC project with no EPiServer and it renders properly client side and server side using the extension, so the component is not the issue. I found something saying that this error is usually seen when React or ReactDOM is not exposed to the global scope so I set it up to ensure that my react component is being added to the global scope, but no luck there either.
Here is my ReactConfig:
public static void Configure()
{
JsEngineSwitcher.Current.DefaultEngineName = V8JsEngine.EngineName;
JsEngineSwitcher.Current.EngineFactories.AddV8();
ReactSiteConfiguration.Configuration
.AddScript("~/Static/js/Components/PrimaryCallout.jsx");
}
Here is my component:
class PrimaryCallout extends React.Component {
constructor(props) {
super(props);
this.state = {
header: this.props.header,
buttonText: this.props.buttonText,
buttonLink: this.props.buttonLink
};
}
render() {
return (
<div className="primary-callout-container">
<h2 className="primary-callout-header">{this.state.header}</h2>
<a href={this.state.buttonLink} className="primary-callout-link">
<button className="primary-callout-button">{this.state.buttonText}</button>
</a>
</div>
);
}
}
Here is how I'm trying to render my component:
#Html.React("PrimaryCallout", new
{
header = Model.Header,
buttonText = Model.CalloutLinkText,
buttonLink = Url.ContentUrl(Model.CalloutLink)
})
Any and all help or insight is appreciated.
Thank you.
After a lot of digging around I determined the issue I was having was indeed with Babel not with React. The React not initialized error was smothering the true issue. I didn't narrow it down to what in my Babel setup was wrong, but I re-did my bundling and minification setup and now I'm back up and running.
The lesson to take from this for anyone else is if you see this "ReactJS.NET has not been initialised correctly. Please ensure you have called services.AddReact() and app.UseReact() in your Startup.cs file." error then look deeper because it's likely smothering the error message for your true problem.

Ant Design SSR mismatch issue, Server renders Chinese but client EN

I added Ant Design to my react/redux SSR ready application. I used Ant Design's Locale Provider component as they said in documentation. But there is two major problems.
In web console, it says; component rendered Chinese at server but at client it rendered English. So SSR is not compatible. I tried some web configs but didn't resolve the issue.
It says; You are using a whole package of antd, please use https://www.npmjs.com/package/babel-plugin-import to reduce app bundle size. I used babel-plugin-import plugin but didn't change any thing.
I will be grateful to any suggestion.
First you must use ant design "LocaleProvider" component as described in documentation.
import { LocaleProvider } from 'antd';
import enUS from 'antd/lib/locale-provider/en_US';
return <LocaleProvider locale={enUS}><App /></LocaleProvider>;
Then to support Server Side Rendering you must use same pattern for server.js file
const { LocaleProvider } = require('antd')
const enUS = require('antd/lib/locale-provider/en_US')
const body = ReactDOMServer.renderToString(
React.createElement(
LocaleProvider,
{ locale: enUS },
React.createElement(
Provider,
{ store },
React.createElement(
StaticRouter,
{ location: req.url, context: context },
React.createElement(Layout, null, React.createElement(Routes))
)
)
)
)

Categories