I have a node express server. This has a file called server.js that has a route called home that responds to the client with html.
The content of the main div in the HTML is a react component (i.e root component for my App) that has been converted to HTML string.
I have created a renderMarkup function in a separate module to my server module. This contains the code for converting my root component to a HTML string using the renderToString Method. My react component is written in JSX and uses es6 imports so I have converted the renderMarkup module into a node compatible module using Webpack and babel.
The outputted bundle is required inside my server.js file. The renderMarkup function is called inside of my route and inserted into a HTML string using template literal. The entire HTML is returned by my route.
Is there a way in development to dynamically update my server rendered components and see the updates without refreshing the page?
I have tried using webpack-dev-middleware but this only updates my client side bundles not server side bundle. Also even after this is achieved not sure how i would achieve hot reloading
I have an array export in my Webpack config that exports my client side and server side config. My code is below
server.js
const express = require("express");
const app = express();
const path = require("path");
const renderMarkup = require("./react-bundles/home.bundle.js").default;
const middleware = require("webpack-dev-middleware");
const webpack = require("webpack");
const config = require("../../webpack.config.js")
const compiler = webpack(config);
const port = 3000;
const __DEV__ = process.env.NODE_ENV === "development";
if (__DEV__) {
app.use(middleware(compiler, { serverSideRender: true }));
} else {
app.use(express.static(path.resolve(__dirname, "../../public")));
}
app.get("/", (req, res) => {
const { html, styleTags } = renderMarkup();
const templateMarkup = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Experiment Page</title>
${styleTags}
<!-- Your other meta tags, stylesheets, and scripts go here -->
<script defer src="/js/shared.bundle.js"></script>
<script defer src="/js/home.bundle.js"></script>
</head>
<body>
<div id="app">${html}</div>
</body>
</html>`;
res.send(templateMarkup);
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
render markup function
import React from "react";
import App from "../components/home";
import { renderToString } from "react-dom/server";
import { ServerStyleSheet } from "styled-components";
const renderMarkup = () => {
let html = "";
let styleTags = "";
const sheet = new ServerStyleSheet();
try {
html = renderToString(sheet.collectStyles(<App />));
styleTags = sheet.getStyleTags();
} catch (err) {
console.log(err);
} finally {
sheet.seal();
}
return { html, styleTags };
};
export default renderMarkup;
webpack config
const path = require("path");
const { merge } = require("webpack-merge");
const serverConfig = require("./webpack.server.config.js");
const pagesFolder = path.resolve(__dirname, "src/pages");
const __DEV__ = process.env.NODE_ENV === "development";
const commonConf = {
name: "client",
entry: {
home: path.resolve(pagesFolder, "home", "entry", "home.js"),
about: path.resolve(pagesFolder, "about", "entry", "about.js"),
},
output: {
filename: "js/[name].bundle.js",
path: path.resolve(__dirname, "public"),
publicPath: "/",
},
optimization: {
splitChunks: {
chunks: "all",
cacheGroups: {
shared: {
name: "shared",
chunks: "all",
filename: "js/shared.bundle.js",
},
},
},
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-react"],
},
},
},
],
},
};
const devConf = merge(commonConf, {
mode: "development", //dev
devtool: "inline-source-map", //dev
devServer: {
hot: true, //dev
static: path.resolve(__dirname, "public"), //dev
},
});
const prodConf = merge(commonConf, {
mode: "production",
});
const getConf = () => {
if (__DEV__) {
return devConf;
}
return prodConf;
};
module.exports = [getConf(), serverConfig];