Isomorphic Render wont get my css or styling - node.js

I am using react as the client side and node.js as the server side, and using webpack to transpile. When I start the website on local host it is missing all of its styling and css. Any Ideas?
This is my frontend webpack that handles the transpiling of my frontend code
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = {
target: 'node',
externals: [nodeExternals()],
entry: './frontend/src/App',
output: {
path: path.resolve(__dirname, 'backend/build'),
filename: 'bundle.js',
libraryTarget: 'commonjs2',
libraryExport: 'default'
},
module: {
rules: [
{
test: /\.jsx$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react']
}
}
},
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
},
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react']
}
}
},
{
test: /\.css$/,
use: [ 'css-loader'],
sideEffects: true
}
]
}
};
This is my backend/server.js that handles server rendering
const express = require('express');
const React = require('react');
const { renderToString } = require('react-dom/server');
const App = require('../backend/build/bundle');
const { matchPath } = require('react-router-dom');
const app = express();
// This is the middleware that will handle all routes
app.get('*', (req, res) => {
// We create a context object that will be passed to the StaticRouter
const context = {};
// Render the app component to a string
const html = renderToString(
React.createElement(App, {
location: req.url,
context,
})
);
// If the app component set a context.url value, it means the user
// tried to access a protected route that they are not authenticated for.
// In this case, we redirect them to the login page.
if (context.url) {
res.redirect(context.url);
} else {
// Send the rendered HTML as the response
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<div id="root">${html}</div>
<script src="/build/bundle.js"></script>
</body>
</html>
`);
}
});
app.listen(3000, () =\> {
console.log('Server is listening on port 3000');
});
This is my frontend App.js
import React from "react";
import { StaticRouter } from "react-router-dom/server";
import { Footer, Contact, Who, Header } from "./containers";
import { Navbar, Background } from "./components";
import { Route, Routes } from "react-router-dom";
import "./App.css";
const App = (props) =\> {
return (
\<StaticRouter location={props.location} context={props.context}\>
\<Navbar /\>
\<Routes\>
\<Route exact path="/" element={\<Header /\>} /\>
\<Route path="/about" element={\<Who /\>} /\>
\<Route path="/contact" element={\<Contact /\>} /\>
\</Routes\>
\<Footer /\>
\</StaticRouter\>
);
};
export default App;
And this is my index.js
import React from 'react';
import ReactDom from 'react-dom';
import './index.css';
import App from './App';
ReactDom.render(
\<BrowserRouter\>
\<App /\>
\</BrowserRouter\>,
document.getElementById('root')
)
I have tried to add the styling-loader to the webpack config for the frontend but I end up getting an error where the document is not defined. The routing works fine, just no css or images, just basic html is being rendered`

Related

React SSR express,webpack,babel babel doesn't like the CSS in my components

I have been trying to learn how to SSR with React(without using Next.js) I've gotten to the point that I'm seeing the HTML rendered on the page but once I add the css imports to style the component babel start throwing errors (
C:\Users\Frozen\Desktop\ssr stuff\ssr4\src\App.css:1
body {
^
SyntaxError: Unexpected token '{'
)
.babelrc
{
"presets": ["#babel/preset-env", "#babel/preset-react"],
}
webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, './build'),
filename: "bundle.js",
publicPath: "/"
},
devServer: {
port: 3010,
static: true,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ['#babel/preset-env', "#babel/preset-react"]
}
},
},
{
test: /\.(css|scss)$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
publicPath: "/"
})
]
};
server.js
import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './src/App.js';
import path from 'path';
import fs from 'fs';
const app = express();
app.get('/', (req, res) => {
console.log('in /');
fs.readFile(
path.resolve('./build/index.html'),
'utf8',
(err, data) => {
if (err) {
console.log(err);
return res.status(500).send('Internal Server Error');
}
const html = data.replace(
'<div id="root"></div>',
`<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`
)
console.log(html)
return res.send(html);
}
);
});
app.use(express.static(path.resolve(__dirname,'build')));
app.listen(3000, () => {
console.log('server listening on port 3000')
})
I also fail to understand why do I have to put the app.use(static) below the app.get('/') aren't middlewares supposed to go one after another in order top to bottom (if I move the app.use(static) above the app.get('/') where I return the component I don't see the console.log('in /');
index.js
import React from "react";
import ReactDOM from 'react-dom'
import App from "./App";
const appElement = document.getElementById('root');
ReactDOM.hydrate(<App/>, appElement)
I run
npx webpack
npx babel-node server.js
And I get the css error (if I remove the css import I don't get it but I don't have css in the component)
Also is there a way to rebuild and run server.js once I change something ( like when you use create-react-app ) Also any other suggestions on what I can improve in the current setup will be much appreciated.
Install babel-plugin-css-modules-transform npm install --save-dev babel-plugin-css-modules-transform.Then include it in .babelrc "plugins": ["css-modules-transform"]

Express router: TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string

I know I am struggling in this problem.
I am working on a webpack Universal React App but i got this error message and I have no idea where it come from:
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received type function ([Function (anonymous)])
at new NodeError (node:internal/errors:372:5)
at Function.from (node:buffer:323:9)
at ServerResponse.send (C:\Dev\isomorphic-react-redux-router-app\node_modules\express\lib\response.js:193:22)
at eval (webpack://isomorphic-react-redux-router-app/./app/serverside/server.js?:17:19)
at Layer.handle [as handle_request] (C:\Dev\isomorphic-react-redux-router-app\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Dev\isomorphic-react-redux-router-app\node_modules\express\lib\router\route.js:144:13)
at Route.dispatch (C:\Dev\isomorphic-react-redux-router-app\node_modules\express\lib\router\route.js:114:3)
at Layer.handle [as handle_request] (C:\Dev\isomorphic-react-redux-router-app\node_modules\express\lib\router\layer.js:95:5)
at C:\Dev\isomorphic-react-redux-router-app\node_modules\express\lib\router\index.js:284:15
at Function.process_params (C:\Dev\isomorphic-react-redux-router-app\node_modules\express\lib\router\index.js:346:12)
I think it is because of the url on the eval argument:
webpack://isomorphic-react-redux-router-app/./app/serverside/server.js?:17:19
Having a relative path in the middle of the url is not good.
Here are my webpack file:
const path = require('path');
require("core-js/stable");
require("regenerator-runtime/runtime");
const nodeExternals = require('webpack-node-externals');
module.exports = [
{
mode: 'development',
stats: {warnings:false},
target:'web',
entry: {
'/bundle-app': ['core-js','regenerator-runtime/runtime','./app/clientside/client.jsx']
},
output: {
path: path.resolve(__dirname, 'build/dev'),
filename: '[name].js',
publicPath: '/'
},
module: {
rules: [
{
test: /\.jpe?g|png$/,
exclude: /node_modules/,
use: ["url-loader", "file-loader"]
},
{
test: /\.(jsx|js)$/,
include: path.resolve(__dirname, '/'),
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
}]
}
]
},
},
{
name: 'server',
mode: 'development',
target: 'node',
stats: {warnings:false},
externals: [nodeExternals()],
entry: {
'/server/bundle-server': ['core-js','regenerator-runtime/runtime','./app/serverside/server.js'],
},
output: {
path: path.resolve(__dirname, 'build/dev'),
filename: '[name].js',
},
plugins: [],
module: {
rules: [
{
test: /\.(jsx|js)$/,
include: path.resolve(__dirname, '/'),
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
}]
},
]
}
}
]
my express server file:
import express from 'express';
import serverRenderer from './renderSSR.js';
import cors from 'cors';
let app = express();
app.use(cors());
app.use(express.urlencoded({extended:false}));
app.use(express.json());
app.get('/', (req, res) => {
res.status(200).send(serverRenderer());
}); // when the user connect to the root of the server we send the page
app.listen(8080, () => console.log("Example app listening on port 8080!"));
import { renderToString } from 'react-dom/server';
import fs from 'fs';
import App from '../src/App.jsx';
export default function serverRenderer() {
return (req, res, next) => {
const html = renderToString( // on rend côté serveur du pur HTML
<StaticRouter location={req.url}>
<App/>
</StaticRouter>
);
// we read the index.html page and we change the div to insert the app content
fs.readFile('../html/index.html', 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
// after succefuly read the html file, we insert the App component content
var htmlApp = data.replace('<div id="app">','<div id="app">' + html);
return htmlApp;
});
};
}
my client file:
// principal programme côté client, est capable de faire du rendue HTML, sera appelé une deuxième par le client.
import * as React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router} from 'react-router-dom';
import App from '../src/App.jsx';
ReactDOM.render(
<Router>
<App/>
</Router>,
document.getElementById('root')
); // on rend le react dans l'element HTML root
and finally my commun App file:
import React from 'react';
import ReactDOM from 'react-dom';
export default function App(props)
{
return (
<p>Hello App!</p>
)
};
Where does the problem come from?
How to fix it?
Thanks in advance for your responses.
The answer from Heiko TheiBen solve my problem.
Thanks for your attention!

Webpack main.js error "Unexpected token <" when served from express server

I'm trying to serve my react app from an express server with a catch all route so that users can still access react routes when they refresh the page or manually type in a url. However, when I try to serve the app entry point, /dist/index.html, from my express server I get Uncaught SyntaxError: Unexpected token <
When I look at the main.js file in the sources tab of the console, this is what is see:
<!DOCTYPE html>
<html lang="en">
<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>React E-Store</title>
</head>
<body>
<div id="root">
</div>
</body>
<script src="/dist/main.js"></script>
</html>
This is what my server.js file looks like:
const express = require('express')
const path = require('path')
const port = process.env.PORT || 8080
const app = express()
// Handles all routes so you do not get a not found error
app.get('/*', function (request, response){
response.sendFile(path.resolve(__dirname, "dist", "index.html"))
})
app.listen(port)
console.log("server started on port " + port)
and here is my webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
template: './index.html',
filename: 'index.html',
inject: 'body'
})
module.exports = {
entry: './src/app.js',
output: {
path: path.resolve('dist'),
publicPath: '/dist/',
filename: '[name].js'
},
module: {
loaders: [
{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
{ test: /\.jsx$/, loader: 'babel-loader', exclude: /node_modules/ },
{
test: /\.(png|jp(e*)g|svg)$/,
loader: 'url-loader',
options: {
limit: 8000, // Convert images < 8kb to base64 strings
name: 'images/[hash]-[name].[ext]'
}
},
{
test: /\.sass$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "sass-loader" // compiles Sass to CSS
}]}
]
},
plugins: [HtmlWebpackPluginConfig]
}
I appreciate any help

Set initial state of store in an isomorphic react-redux app from NodeJS server (I am using react-router)

I am trying to set the initialState of my redux store from the NodeJS server. I am unsure on what I missed and how to get it to work.
Here's my server.js
const express = require('express');
const path = require('path');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackHotServerMiddleware = require('webpack-hot-server-middleware');
const config = require('./webpack/webpack.development.config.js');
const compiler = webpack(config);
const app = express();
app.use(webpackHotMiddleware(compiler.compilers.find(
compiler => compiler.name === 'client'
)));
app.use(webpackHotServerMiddleware(compiler));
app.listen(3000);
Here's my webpack.development.config
const path = require('path');
module.exports = [
{
name: 'client',
target: 'web',
entry: './routes/client.jsx',
output: {
path: path.join(__dirname, 'assets'),
filename: 'client.js',
publicPath: '/assets/',
},
resolve: {
extensions: ['.js', '.jsx']
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules\/)/,
use: [
{
loader: 'babel-loader',
}
]
},
{
test: /\.scss$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[name]__[local]___[hash:base64:5]',
sourceMap: true
}
},
{
loader: 'sass-loader'
}
]
},
{ test: /\.html$/, loader: "html-loader" }
],
},
},
];
PS, the same modules are there for server, but I've removed in an attempt to keep it short.
Here's the client.jsx
import React from 'react';
import { createStore } from 'redux'
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import Routes from './routes.jsx';
import { Provider } from 'react-redux';
import MainStore from '../views/store/MainStore';
import home from './home';
const store = createStore(MainStore, "Name");
ReactDOM.render((
<Provider store={store}>
<BrowserRouter>
<Routes />
</BrowserRouter>
</Provider>
), document.getElementById('root'));
And finally my home.jsx
const express = require('express');
const path = require('path');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackHotServerMiddleware = require('webpack-hot-server-middleware');
const config = require('../webpack/webpack.development.config.js');
const router = express();
router.get('/test', (req, res) => {
res.json({ message: "I'm just testing to see if this works" });
});
const name = "Mona";
module.exports = {
router: router,
name: name,
};
I would like to import const name from home.jsx to client.jsx and set the initial value of the store as this imported value. I could do it manually by the line
const store = createStore(MainStore, "Name");
in client.jsx. But instead of using "Name" as a string, I would like to replace it with the constant value in home.jsx.
Any suggestions?
Set initial state of store in an isomorphic react-redux app
Typical solution for supposed task is using preloaded state, which had been calculated at SSR phase and injected as serialized informational field in rendered HTML-template. Also you can create dedicated resource, like /api/initialState.js, which create initial state for your view model, dependent on client-side request and maybe JWT credentials.
You can see similar solution in resolve-scripts npm package, which provides ability for full-automatic isomorphic storage for redux on server, including SSR.

Angular2 Router and Express Integration

I'm having a problem using the Angular2 router and express, but none of the previous questions seem to have the solution I need. I'm using the latest version of the angular2-cli.
When I direct my browser to localhost:3000, I get the generic "app works" message that the angular2-cli generates. My problem arises when I try to navigate to one of my child routes, such as localhost:3000/auth/login - it redirects me back to the same "app works" page, instead of showing me a page that says "login works".
Looking at other questions on stack exchange, I figure that the issue is here:
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
When I replace the * with a /, instead of redirecting me back to my target page, it gives me the cannot GET error.
In summary, my problem is that url navigation of my angular2 routes integrated into my express app is not working.
My express code in server.js is as follows:
var express = require("express");
var app = express();
var bodyParser = require("body-parser");
var mongoose = require("mongoose");
var path = require("path");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}));
app.use(express.static(path.join(__dirname, 'dist')));
// Catch all other routes and return the index file
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
app.listen(3000, () => {
console.log("Server has started");
});
My application's routing module is as follows (app-routing.module.ts):
import {Route, RouterModule} from '#angular/router';
import {NgModule} from '#angular/core';
import {LoginComponent} from './login/login.component';
import {RegisterComponent} from './register/register.component';
import {FeedComponent} from './feed/feed.component';
import {FindComponent} from './find/find.component';
import {SubmitComponent} from './submit/submit.component';
import {ProfileComponent} from './profile/profile.component';
export const routeConfig = [
{
path: "auth",
children: [
{
path: "login",
component: LoginComponent
},
{
path: "register",
component: RegisterComponent
},
{
path: "",
redirectTo: "/login",
pathMatch: "full"
}
]
},
{
path: "app",
children: [
{
path: "feed",
component: FeedComponent
},
{
path: "find",
component: FindComponent
},
{
path: "submit",
component: SubmitComponent
},
{
path: "profile",
component: ProfileComponent
},
{
path: "",
redirectTo: "/feed",
pathMatch: "full"
}
]
}
];
#NgModule({
imports: [RouterModule.forRoot(routeConfig)],
exports: [RouterModule]
})
export class AppRoutingModule{
}
My application module is as follows:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import {AppRoutingModule} from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { FeedComponent } from './feed/feed.component';
import { FindComponent } from './find/find.component';
import { SubmitComponent } from './submit/submit.component';
import { ProfileComponent } from './profile/profile.component';
#NgModule({
declarations: [
AppComponent,
LoginComponent,
RegisterComponent,
FeedComponent,
FindComponent,
SubmitComponent,
ProfileComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
All the components have been defined in their respective files and are the barebones default templates generated by the angular2-cli.
I have no idea what to do at this point. I've even tried restarting and rewriting this project multiple times to see if I went wrong somewhere (this is my 3rd attempt).
For angular2 and nodejs integration you can either put this in server.js:
var cors = required('cors');
app.use(cors());
or you can make use of proxy in your web server. For example in apache2 inside virtualhost tag you can do
ProxyPass /api/ http://localhost:3000/
ProxyReverse /api/ http://localhost:3000/
Now instead of using "http://localhost:3000/" in your http request function (although you are not using it) you can use just "api/".

Resources