Server-side render does not render UI library - javascript

I am trying to implement a server-side rendering for my app. It used to be a client side render using ReactDom. Everything works fine in client-side render, but now when I make it server-side render, none of the CSS sheet and UI libraries(Reactstrap + Material UI) are rendered. The UI broke completely.
I am injecting my App.js(actual front-end page with all the UI) into my html.js(a template)
My server:
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server'
import App from '../../src/App'
import Html from '../../src/html.js'
const app = express();
// console.log that your server is up and running
app.listen(3001, () => console.log(`Listening on port 3001`));
app.get('/',(req,res) => {
const title="2FA SDK"
const body = renderToString(<App/>)
res.send(
Html({
body,
title,
})
)
})
My html.js(just a simple template):
const Html = ({ body, styles, title }) => `
<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
</head>
<body style="margin:0">
<div id="app">${body}</div>
</body>
</html>
`;
My app.js(using material ui, minimal)
render(){
return(
<div className="container">
<div>
<TextField
label="Application Name"
type="text"
variant="outlined"
onChange={this.handleApplicationName}
/>
</div>
<div>
<FormLabel>Phone</FormLabel>
<div>
<OutlinedInput
value={this.state.textmaskArray[2]}
onChange={(e) => this.handlePhoneNumber(e,2)}
labelWidth={200}
/>
</div>
</div>
<Button variant="contained" color="primary" onClick={this.props.buttonAction}>
</Button>
</div>
Part of App.js that uses reactstrap:
render(){
return(
<CardControl
title={"Code Authention"}
message={"For your security, we need to verify your identity by sending a code to your phone number"}
>
<Container>
<Row>
<Col>
<div>{this.props.phoneList}</div>
</Col>
<Col>
<div>{this.props.methodList}</div>
</Col>
</Row>
<Button className="sm-2" disabled={this.props.disabled} color="info" onClick={this.props.buttonAction}>{this.props.buttonName}</Button>
</Container>
</CardControl>
)
}
My webpack.config.js:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
module.exports = [
{
/*Config for backend code*/
entry: './src/server/server.js',
target: 'node',
output: {
filename: 'server.js'
},
externals: [nodeExternals()],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: {
loader: "html-loader",
options: { minimize: true }
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader,"css-loader"]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./public/index.html",
filename:"./index.html"
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename:"[id].css"
})
]
},
]
What I am using: Create-react-app
What I have tried: I installed CSS loders, but it is not working at all.
I also looked up material-ui's document on server-side rendering, I feel like I am just missing a small part and the instruction they provided may be a overkill.
What I need help with: I am trying to figure out why the UI component from material-ui or bootstrap did not get render at all. I am suspecting that nodeExternal exclude them, but I am not sure.

You will need to hydrate your rendered HTML in the client side in order to have react take over.
Create a client.js file with:
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.hydrate(
<App/>,
document.querySelector('#app'),
);
note that the hydrate should match what renderToString rendered.
Next add this as an additional entry in your webpack configuration:
module.exports = [
{
/*Config for backend code*/
entry: './src/server/server.js',
target: 'node',
output: {
filename: 'server.js'
},
externals: [nodeExternals()],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: {
loader: "html-loader",
options: { minimize: true }
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader,"css-loader"]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./public/index.html",
filename:"./index.html"
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename:"[id].css"
})
]
},
{
entry: './client.js',
output: {
filename: 'bundle.js',
},
module: {
rules: [ {
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
],
},
]
Change your html rendering code to include this:
const Html = ({ body, styles, title }) => `
<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
</head>
<body style="margin:0">
<div id="app">${body}</div>
<script async src="/bundle.js"></script>
</body>
</html>
`;
I'm not 100% confident on whether this exact code will work but this is the general idea.

Related

React not rendering components

I am dealing with an electron BrowserWindow that should render an HTML file filled with some react components. However the React components are not showing.
I have an html file which is:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self'" />
</head>
<body>
<div id='QA_Dialog'></div>
<script src="../js/index.js"></script>
</body>
</html>
My script source file "../js/index.js" contains some easy React rendering:
import ReactDOM from 'react-dom';
import QAWindow from './QAWindow';
document.body.style.overflow = "hidden";
document.body.style.borderRadius = "5px";
ReactDOM.render(<QAWindow />, document.getElementById('QA_Dialog'))
Where QAWindow is :
import React from 'react';
import { electron } from 'webpack';
import { CloseButton, StyledButton} from '../templates/style';
const useState = React.useState
function QuestionForm() {
const [question, setQuestion] = useState()
function handleSubmit(e) {
e.preventDefault()
electron.QandA.askQuestionSignal(question);
}
function handleClose(e){
e.preventDefault()
electron.QandA.closeWindowSignal('Dio')
}
return (
<>
<CloseButton onClick={handleClose}>
<img src="../templates/close.svg" />
</CloseButton>
<form onSubmit={handleSubmit}>
<input value={question} onChange={e => setQuestion(e.target.value)} placeholder="Ask a question..." />
<span>
<StyledButton>Find Answer</StyledButton>
</span>
</form>
</>
)
}
export default function QAWindow() {
return(
<>
<QuestionForm />
</>
)
}
If I change the above file to only export a simple element it doesn't work anyways. So I assume that the problem is not in QAWindow.
These files are copied in the build/ folder, and there, the reference '../js/index.js' is still valid (the structure of the files dosn't change).
../js/index.js got compiled by web-pack using a babel-loader.
Why does this render a white page???
EDIT:
To better debug this, I am also providing my webpack.config.js:
// This first configuration bundles the script that renders the react components
const QandAExtension= {
mode: 'development',
entry: './src/preloads/QandA/js/index.js', // This entry point match the correct react script that needs to be bundled.
devtool: 'inline-source-map',
target: 'electron-renderer',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [[
'#babel/preset-env', {
targets: {
esmodules: true
}
}],
'#babel/preset-react']
}
}
},
{
test: [/\.s[ac]ss$/i, /\.css$/i],
use: [
// Creates `style` nodes from JS strings
'style-loader',
// Translates CSS into CommonJS
'css-loader',
// Compiles Sass to CSS
'sass-loader',
],
},
{
test: /\.svg$/,
use: [
{
loader: 'svg-url-loader',
options: {
limit: 10000,
},
},
],
}
]
},
resolve: {
extensions: ['.js'],
},
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'build', 'QandA', 'js'),
},
};
// This second configuration copies the folder containing the HTML file
// into the build/ folder of this app.
const preloadConfig = getConfig({
target: 'electron-renderer',
devtool: false,
watch: dev,
entry: {
'view-preload': './src/preloads/view-preload',
},
// Here in plugin you can specify what you want to copy in the build/ folder.
plugins: [
new CopyPlugin({
patterns: [
{
from: join(__dirname, "src", "preloads", "QandA", "templates"),
to: "QandA/templates",
toType: "dir",
}
],
}),
],
},);
module.exports = [preloadConfig, QandAExtension];
You need to import React's Javascript bundles, which React does through the command line react-scripts start (and isn't explicitly defined in the index.html file).
e.g.
<script src="/static/js/bundle.js"></script>
<script src="/static/js/0.chunk.js"></script>
<script src="/static/js/main.chunk.js"></script>
are imports on functioning index.html React pages.

Storybook + MDX

My .js stories show perfectly on storybook but .mdx stories do not show & I get no error as well
main.js
module.exports = {
addons: [
'#storybook/addon-docs/register',
'#storybook/addon-actions/register',
'#storybook/addon-links/register',
'#storybook/addon-knobs/register',
'#storybook/addon-storysource/register',
'#storybook/addon-viewport/register',
],
};
webpack.config.js
as suggested here https://www.npmjs.com/package/#storybook/addon-docs#manual-configuration
config.module.rules.push({
test: /\.(stories|story)\.mdx?$/,
use: [
{
loader: 'babel-loader',
options: {
plugins: ['#babel/plugin-transform-react-jsx'],
},
},
{
loader: '#mdx-js/loader',
options: {
compilers: [createCompiler({})],
},
},
],
});
config.module.rules.push({
test: /\.(stories|story)\.jsx?$/,
loader: require.resolve('#storybook/source-loader'),
exclude: [/node_modules/],
enforce: 'pre',
});
preview.js
addParameters({
docs: {
container: DocsContainer,
page: DocsPage,
},
});
function loadStories() {
const requires = [require.context('#rrc', true, /\.stories\.(js|mdx)$/)];
for (const req of requires) req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);
Now I have 2 types of stories
story.mdx
import { Meta, Story } from '#storybook/addon-docs/blocks';
// Components
import Chips from '../';
<Meta title="AAAAAAAAA AAAAAA/Chip2" component={Chip} />
# Chip
I can define a story with the function imported from CSF:
And I can also embed arbitrary markdown & JSX in this file.
<Chip label="Basic" />
<Preview>
<Story name="all checkboxes">
<form>
<Chip label="Basic" />
</form>
</Story>
</Preview>
### Installing
A step by step series of examples that tell you how to get a development env running
and .js stories which are like
import React from 'react';
import { storiesOf } from '#storybook/react';
import { Chip } from '../../..';
storiesOf('Chip', module)
.addParameters({
component: Chip,
})
.add('Basic', () => <Chip label="Basic" />)
.add('Disabled', () => <Chip label="Disabled" disabled />)

class name not working in button tag in react js

i'm trying to add style to button using classNamein react js. but it didn't work. it shows default style like below. can anyone help with this?
here is my code.
home.js
import React from "react";
export class Home extends React.Component{
constructor(props){
super();
this.age=props.age;
}
onMakeOlder(){
this.age +=3;
console.log(this.age);
}
render(){
return (
<div>
<p>in a new component!</p>
<p>your name is {this.props.name} , your age is {this.age}</p>
<hr/>
<button onClick={this.onMakeOlder.bind(this)} className="btn btn-primary">Make me older!</button>
</div>
);
}
}
Home.propTypes={
name: React.PropTypes.string,
age: React.PropTypes.number
/*user: React.PropTypes.object*/
};
Edited
here is main page.
index.js
import React from "react";
import { render } from "react-dom";
import {Header} from "./components/Header";
import {Home} from "./components/Home";
class App extends React.Component {
render() {
/* var user={
name:"Senarath",
hobbies: ["games","sports"]
};*/
return (
<div className="container">
<div className="row">
<div className="col-xs-10 col-xs-offset-1">
<Header/>
</div>
</div>
<div className="row">
<div className="col-xs-10 col-xs-offset-1">
<Home name={"Pasindu"} age={24} />
</div>
</div>
</div>
);
}
}
render(<App/>,window.document.getElementById("app"));
webpack.config.js
var webpack =require("webpack");
var path = require("path");
var DIST_DIR = path.resolve(__dirname, "dist");
var SRC_DIR = path.resolve(__dirname, "src");
var config = {
entry: SRC_DIR + "/app/index.js",
output: {
path: DIST_DIR + "/app",
filename: "bundle.js",
publicPath: "/app/"
},
module: {
loaders: [
{
test: /\.js?/,
include: SRC_DIR,
loader: "babel-loader",
query: {
presets: ["react", "es2015", "stage-2"]
}
},
{
test: /\.css$/,
loaders: ['style-loader', 'css-loader'],
},
],
}
};
module.exports = config;
here is the error i get.
it looks like you use bootstrap. if you also use webpack and node_modules (this folder should be in your project's root directory) then see if the bootstrap folder is there. if it is, then you can connect it that way:
import React from "react";
import "bootstrap/dist/css/bootstrap.css";
if this folder is not there - then install it through the command npm install bootstrap and you should see the appropriate styles.
also you should add css-loader in your rules section of webpack config:
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
https://github.com/webpack-contrib/css-loader
EDIT:
for old versions of webpack use loaders keyword:
module: {
loaders: [
{
test: /\.js?/,
include: SRC_DIR,
loader: "babel-loader",
query: {
presets: ["react", "es2015", "stage-2"]
}
},
{
test: /\.css$/,
loaders: ['style-loader', 'css-loader'],
},
],
}
You can easily grab the button via type. ClassName did not work for me, too.
The "type"-solution works excellent for me:
HTML:
<button type="no-margin">Button</button>
CSS:
button,
[type="no-margin"] {
margin: 0;
}
Write a css file inside public folder. and link that css file in to your index.html file.
public/css/custom.css
.some-class {
'color' : red
}
public/index.html
<link href="css/custom.css" rel="stylesheet" type="text/css" />
then use that css calss in side className="some-class"
OR
you can directlly do this
style = {{'color' : 'red'}}
OR
render() {
const text-color = {
'color' : 'red'
}
return <div style={text-color}></div>
}

Cannot import react js component

I trying to import a react component to jsx file but it throws this exception:
This is my main code:
import React, { Component } from "react";
import Sidebar from "./Sidebar";
class App extends Component{
render(){
return (
<Sidebar/>
);
}
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
and this is my Sidebar component:
import React, { Component } from "react";
export default class Sidebar extends Component{
render(){
return (
<h1>Hello Sidebar</h1>
);
}
}
My folders structure:
I post simpler version which I know does work:
./index.js :
import React from 'react';
import ReactDOM from 'react-dom';
import Application from './components/Application'
ReactDOM.render(<Application />, document.getElementById('root'));
./components/Application :
import React from 'react';
class Application extends React.Component {
render() {
return (
<div className="App">
My Application!
</div>
);
}
}
export default Application;
This should be everything needed to make it work.
If you want to shorten the above even more, by removing the export line at the bottom, a less traditional approach would be defining the class like this...
export default class Application extends React.Component {...}
Looks like you haven't added rule for .jsx file in webpack.config.js.
Since you have both .js and .jsx files you need to tell webpack to load files with extension .js and .jsx. Add below config to webpack.config.js in rules section
{
//tell webpack to use jsx-loader for all *.jsx files
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "babel-loader"
}
And add extensions like
resolve: {
modules: [
path.resolve("./src"),
path.resolve("./node_modules")
],
extensions: [".js", ".jsx"]
}
Below is the working webpack.config.js file for your ref
module.exports = {
target: "web",
entry: [
"whatwg-fetch",
'webpack-dev-server/client',
'webpack/hot/only-dev-server',
'babel-polyfill',
"./src/index.js"
],
output: {
path: __dirname + 'build',
publicPath: '/',
filename: "bundle.js"
},
plugins: [new HtmlWebpackPlugin({
template: "index.html"
}),
new CompressionPlugin({
asset: "[path].gz[query]",
algorithm: "gzip",
test: /\.js$|\.jsx$|\.css$|\.html$/,
threshold: 10240,
minRatio: 0.8
}),
new webpack.HotModuleReplacementPlugin(),
// enable HMR globally
new webpack.NoEmitOnErrorsPlugin()
],
module: {
rules: [
{
//tell webpack to use jsx-loader for all *.jsx files
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "babel-loader"
},
{
test: /\.css$/,
loader: "style-loader!css-loader"
},
{
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/,
use: [
{
loader: 'file-loader',
options: {}
}
]
},
{
test: /\.(eot|ttf)$/,
loader: "file-loader",
},
{
test: /\.scss$/,
loaders: ["style-loader", "css-loader", "sass-loader"]
}
]
},
resolve: {
modules: [
path.resolve("./src"),
path.resolve("./node_modules")
],
extensions: [".js", ".jsx"]
},
devServer: {
watchOptions: {
// Needed for Windows Subsystem for Linux dev environment:
poll: true
},
contentBase: './build'
},
devtool: "cheap-module-eval-source-map",
node: {
child_process : "empty",
fs: "empty"
}
};

React do not render new components, new webpack setup

Today I was setting up my first Webpack Bebel React project and I got some strange case here.I don't know why, but every single Component that I make is not recognized by React. I can see it directly in the inspector, and It seems like it's not getting compiled. All standard HTML elements are getting rendered. Even console.log inside of constructor function of a component that I have created is not called. I run Hot mode with webpack -p
Here is my Webpack config:
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const webpack = require('webpack')
const path = require('path')
const isProduction = process.env.NODE_ENV === 'production'
const cssDeveloperLoaders = ['style-loader', 'css-loader', 'sass-loader']
const cssProduction = ExtractTextPlugin.extract({
fallback: 'style-loader',
loader: ['css-loader', 'sass-loader'],
publicPath: '/dist'
})
const cssConfig = isProduction ? cssProduction : cssDeveloperLoaders
module.exports = {
entry: {
app: './src/app.jsx'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.scss$/,
use: cssConfig
},
{
test: /\.jsx$/,
exclude: path.resolve(__dirname + '/node_modules/'),
use: 'babel-loader',
query: {
presents: ['es2015','react']
}
}
]
},
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
hot: true,
open: true,
openPage: '' //Fix to webpack version 3.0.0 after removing redirection to /undefined
},
plugins: [
new ExtractTextPlugin({
filename: 'app.css',
disable: !isProduction, //So if production is running it will generate file otherwise not
allChunks: true
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin()
]
}
My .bablerc
{
"presets": [
"es2015",
"react"
]
}
App.jsx:
import './app.scss'
import React from 'react';
import { render } from 'react-dom';
import engine from './engine.jsx'
render(
<engine/>,
document.getElementById('root')
);
engine.jsx
import React from 'react';
class engine extends React.Component{
constructor(){
super();
console.log('Component has been constructed ')
}
render(){
return(
<div>xD</div>
)
}
}
export default engine;
The picture of React Chrome extension.
Please notice, console.log is not been called.
My html is empty, I see only engine element (Not compiled.)
Any suggestions about this problem?
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="root"></div>
<script src="app.bundle.js"></script>
</body>
</html>
In your webpack config file add
resolve : {
extensions: [".js", ".jsx"]
}
So that you won't need to import your jsx file with extenstion.
Class names should start with Capital letters otherwise methods in react components will not be invoked and also no error will be thrown.
engine.jsx
class Engine extends React.Component{
constructor(){
super();
console.log('Component has been constructed ')
}
render(){
return(
<div>xD</div>
)
}
}
export default Engine;
App.jsx
import Engine from './engine'
render(
<Engine/>,
document.getElementById('root')
);
Please verify https://codesandbox.io/s/D9rpvWWG6
Also you can refer https://github.com/facebook/react/issues/4695

Categories

Resources