Blank page on refresh when using nested path - node.js

I'm pretty new to react and webpack, and I'm trying to use react-router-dom for routing in my web app.
But something weird happens, when I defined the path for my component to '/:guid' everything works fine, but if I set it to '/users/:guid' I'm getting a blank page when I refresh the page.
I read some posts that said to add 'publicPath' and 'historyApiFallback: true' to my webpack.config.js but it still doesn't work for some reason. although, before adding it, I got 'cannot GET url' error.
Router.js:
import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import UserInfo from '../Containers/UserInfo/UserInfo';
import Main from './Main/Main';
const Router = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path='/' component={Main} />
<Route path='/users/:guid' component={UserInfo} />
</Switch>
</BrowserRouter>
);
};
export default Router;
webpack.config.js:
const path = require('path');
const DIST_DIR = path.resolve(__dirname, 'dist');
const SRC_DIR = path.resolve(__dirname, 'src');
const config = {
entry: `${SRC_DIR}/app/index.js`,
output: {
path: `${DIST_DIR}/`,
filename: 'bundle.js',
publicPath: '/'
},
module: {
rules: [
{
test: /\.js?$/,
include: SRC_DIR,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-2']
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
devServer: {
historyApiFallback: true
}
};
module.exports = config;

Well, after some more research, I got to this github webpack-dev-server issue page that suggests to add a <base /> to my "index.html" file:
<!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 Training Project</title>
<base href="/">
</head>
<body>
<div id="app"></div>
<script src="./bundle.js"></script>
</body>
</html>
Apparently this is what I was missing.

This worked for me, thank you
<base href="/">

Related

How to render .ejs files using webpack?

I am having the issue with trying to use .ejs files with Webpack. I have tried numerous packages and haven't managed to figure it out. I have five files that I want to render and have a layout.ejs which acts as a base for my application. I am using ejs-loader as it is the most popular. When I run webpack, I get the following error:
ERROR in Template execution failed: ReferenceError: body is not defined
I am relatively new to module bundling and have tried to follow the docs as close as possible.
webpack.config.js
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const nodeExternals = require('webpack-node-externals')
const config = {
target: 'node',
mode: 'development',
entry: './app.js',
externals: [nodeExternals()],
output: {
path: path.join(__dirname, './dist'),
filename: 'bundle.js'
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.ejs$/,
loader: 'ejs-loader?variable=data'
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: './views/layout.ejs',
hash: true,
}),
new webpack.ProvidePlugin({
_: "underscore"
})
]
}
module.exports = config;
layout.ejs
<!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" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
crossorigin="anonymous">
<link rel="stylesheet" href="https://bootswatch.com/4/flatly/bootstrap.min.css" />
<title>MyApp</title>
</head>
<body>
<div class="container">
<%- body %>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
crossorigin="anonymous"></script>
<script src="../dist/bundle.js"></script>
</body>
</html>

Express Static files on server not loading correctly but React Front end is loading correctly?

I have a ReactJS project with Node/Express serving my server. On my front end (React) is serving port 3312 and on my server it's serving port (5000). When I load my front end through port 3312, everything looks great and my react router routes works fine. (My api's work everything is great). However, when I try and serve static files and see if I get the same result through my server (port 5000) I only see the styles on my page. (I have a background color) I don't see any of the html whatsoever that the static files should be serving?
I get no errors in the console when I look at localhost:5000. However, my css styles are displaying on the page correctly (cause I have a background color set on my body). However, I cannot see any of my front end React displaying html code. I went inside my index.html file and put a simple test in the root div and it's displaying but I don't understand why my React code isn't displaying on my server.
I most likely think the problem is with the express static files not serving my images or React Router code. I should also note that I'm not using create-react-app I'm using webpack dev server react no cli.
Also, I'm not using create-react-app I'm using no cli for custom webpack.
Here is my code:
(Node Server)
const express = require("express");
const path = require("path");
const router = express.Router();
const app = express();
const port = 5000;
// Serve static files on server
app.use("/public", express.static(__dirname + "/../public"));
app.get("*", function(request, response) {
response.sendFile(path.join(__dirname + "/../public/index.html"));
});
if (app.get("env") === "development") {
app.listen(port, () => {
console.log(`Server started on port ${port}`);
});
} else {
app.listen(port, "171.33.4.126", () => {
console.log(`Server started on port ${port}`);
});
}
routes.js
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomePage from "./components/homepage";
import AboutPage from "./components/aboutpage";
import Contactpage from "./components/contactpage";
export default (
<Router>
<div>
<Switch>
<Route exact path="/" component={Landingpage} />
<Route exact path="/about" component={AboutPage} />
<Route exact path="/contact" component={Contactpage} />
</Switch>
</div>
</Router>
);
index.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import Routes from "./routes";
ReactDOM.render(<div>{Routes}</div>, document.getElementById("root"));
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link
href="/public/assets/css/styles.css"
rel="stylesheet"
type="text/css"
/>
<title>Site</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
Webpack Configuration Development
const webpack = require("webpack");
const path = require("path");
// const HtmlWebPackPlugin = require("html-webpack-plugin");
const BrowserSyncPlugin = require("browser-sync-webpack-plugin");
module.exports = {
devServer: {
historyApiFallback: true,
port: 3312,
proxy: {
"/api": "http://localhost:5000"
}
},
entry: ["babel-polyfill", __dirname + "/src/index.js"],
output: {
path: path.join(__dirname, "/public"),
filename: "bundle.js",
publicPath: "/"
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
query: {
presets: ["react", "env", "stage-0"]
}
}
},
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" },
{ loader: "sass-loader" }
]
}
]
},
plugins: [
// new HtmlWebPackPlugin({
// template: "./public/index.html"
// }),
new BrowserSyncPlugin({
host: "localhost",
port: 3312,
files: [
"./public/*.html",
"./public/assets/scss/*.scss",
"./public/assets/variables/*.scss",
"./public/assets/mixins/*.scss",
"./public/assets/reset/*.scss"
],
proxy: "http://localhost:3312/"
})
]
};
Here is a screenshot of my folder structure:
Screenshot of network status in console:
Your react code is not showing up because index.html is not including it. Is public/bundle.js your transpiled react code? If so, add the following line to index.html, right after <div id="root"></div>:
<script src="/public/bundle.js"></script>
If not, then change the path pointed by src to the correct one.
You can see your css styles just fine because you are already including those in index.html.
UPDATE: a couple of points based on your webpack config:
Change the port in which you are running webpack-dev-server to 3000. You will also have to change BrowserSyncPlugin's proxy to http://localhost:3000/. You can then go to localhost:3312 in your browser and requests will be proxied to the webpack-dev-server running on port 3000.
Correct me if I'm wrong, but you have not posted your index.html in its entirety. I'm guessing you already have a line that looks like <script src="/bundle.js"></script> somewhere in that .html file -- based on your latest comment, it seems it is being added by HtmlWebPackPlugin. webpack-dev-server is serving bundle.js through /, the publicPath you've specified in webpack's output option. This works just fine as long as you access your site through localhost:3312 or localhost:3000. However, your express.js server running on port 5000 has no idea webpack-dev-server is serving bundle.js at /; as a result of that, you end up seeing none of your react code when you go to localhost:5000. As for the styles, you are already serving them correctly through /public/assets/css, which the express.js server understands because you've specified that in this line: app.use("/public", express.static(__dirname + "/../public")). The solution is to change the line <script src="/bundle.js"></script> in your .html file to:
<script src="http://localhost:3312/bundle.js"></script>
Also, in webpack's output option, change the publicPath to http://localhost:3312/. Now, as long as you have webpack-dev-server running (with BrowserSyncPlugin), you can access
your site at localhost:5000 and your bundle.js file should be
served just fine.
Try the following:
Webpack Config
module.exports = {
devServer: {
contentBase: path.join(__dirname, "/public"),
historyApiFallback: true,
port: 3000,
proxy: {
"/api": "http://localhost:5000"
}
},
entry: ["babel-polyfill", __dirname + "/src/index.js"],
output: {
path: path.join(__dirname, "/public"),
filename: "bundle.js",
publicPath: "http://localhost:3312/"
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
query: {
presets: ["react", "env", "stage-0"]
}
}
},
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" },
{ loader: "sass-loader" }
]
}
]
},
plugins: [
new BrowserSyncPlugin({
host: "localhost",
port: 3312,
files: [
"./public/*.html",
"./public/assets/scss/*.scss",
"./public/assets/variables/*.scss",
"./public/assets/mixins/*.scss",
"./public/assets/reset/*.scss"
],
proxy: "http://localhost:3000/"
})
]
};
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link
href="/assets/css/styles.css"
rel="stylesheet"
type="text/css"
/>
<title>Site</title>
</head>
<body>
<div id="root"></div>
<script src="http://localhost:3312/bundle.js"></script>
</body>
</html>
Oh sorry, it looks like the problem is in index.js. Change {Routes} to <Routes />
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import Routes from "./routes";
ReactDOM.render(<div><Routes/></div>, document.getElementById("root"));
You have another problem in routes.js. Export a component like
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomePage from "./components/homepage";
import AboutPage from "./components/aboutpage";
import Contactpage from "./components/contactpage";
// make this a component
export default ()=>(
<Router>
<Switch>
<Route exact path="/" component={Landingpage} />
<Route exact path="/about" component={AboutPage} />
<Route exact path="/contact" component={Contactpage} />
</Switch>
</Router>
);

Express static middleware returning error in log

I have a completed project done on my local machine and am currently learning how to deploy it to production. I figured out a lot of things in the documentation for production and such but I have been stuck for awhile now on the part on how to tell my server to serve my static assets that contain my build files that I executed with web pack. For the record, I'm not using create-react-app I'm using the webpack-dev-server cli.
I've only ever connected to my server successfully and ran routes through it using MySQL but I never tested serving the static files on the server when my development was ready for production. I'm getting an error in the console when I try to serve my static build files. I can't tell if the error is because I have my routes being served wrong on my server or there is an issue in my index.html build.
Here is my code the server file is big so I'll only show the relevant parts pertaining to the issue I'm stuck on:
Server file:
const express = require("express");
const bodyParser = require("body-parser");
const path = require("path");
const cors = require("cors");
const router = express.Router();
const app = express();
const port = 5000;
app.use(cors());
// Body Parser Middleware
app.use(bodyParser.json({ limit: "50mb" }));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.get("*", function(request, response) {
response.sendFile(path.join(__dirname + "/../public/index.html"));
});
// Serve static files on server
app.use("../", express.static(__dirname + "public/index.html"));
app.listen(port, () => {
console.log(`Server started on port ${port}`);
});
Build Index.html File
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#000000">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
<link href='https://fonts.googleapis.com/css?family=PT+Sans:400,400italic,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/v4-shims.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css" rel="stylesheet">
<link rel="stylesheet" href="/public/assets/css/pignose.calendar.min.css">
<link href="/public/assets/css/styles.css" rel="stylesheet" type="text/css">
<title></title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="/public/assets/js/jquery-3.3.1.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"></script>
<!-- Ck Editor Library -->
<script src="/public/assets/js/plugins/ckeditor/ckeditor.js"></script>
<!-- Pignose Calendar -->
<script src="/public/assets/js/pignose.calendar.full.min.js"></script>
<script type="text/javascript" src="/public/assets/js/scripts.js"></script>
</body>
</html>
Webpack Production
const webpack = require("webpack");
const path = require("path");
const CompressionPlugin = require("compression-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
const HtmlWebPackPlugin = require("html-webpack-plugin");
var browserConfig = {
entry: ["babel-polyfill", __dirname + "/src/index.js"],
output: {
path: path.resolve(__dirname + "/build"),
filename: "bundle.js",
publicPath: "/"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
query: {
presets: ["react", "env", "stage-0"]
}
}
},
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" },
{ loader: "sass-loader" }
]
}
]
},
plugins: [
new CompressionPlugin({
filename: "[path].gz[query]",
algorithm: "gzip",
minRatio: 0.8,
threshold: 8192
}),
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify("production")
}),
new HtmlWebPackPlugin({
template: "./public/index.html"
})
]
};
var serverConfig = {
target: "node",
externals: [nodeExternals()],
entry: __dirname + "/server/main.js",
output: {
path: path.resolve(__dirname + "/build"),
filename: "bundle.js",
publicPath: "/"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
query: {
presets: ["react", "env", "stage-0"]
}
}
}
]
}
};
module.exports = [browserConfig, serverConfig];
Folder Architecture
Error log I'm getting when I try to connect to my port for my build files:
second error:
In express middleware configurations order matters, since app.get("*", function(request, response) is in the top every request will go through that and send the index.html. Change the order like this
// Serve static files on server
app.use('/public', express.static(__dirname + "/../public"));
app.get("*", function (request, response) {
response.sendFile(path.join(__dirname + "/../public/index.html"));
});
and it should work
Edit
As your request to extend my answer, what was happening here is since you haven't configured the public direcotry, express server will get to the next request handler which is app.get("*" , ... which sends the index.html as the response. So when you request this javascript file
<script type="text/javascript" src="/public/assets/js/jquery-3.3.1.min.js"></script>
It will send the index.html as i mentioned without sending the jquery-3.3.1.min.js and same for every js and css files, but your browser is expecting a javascript file and try to parse it so the file <!doctype> .. is parsing and since it is not a valid javascript, it prints the error. Hope that helps

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

React is not defined ReferenceError? I am using webpack and webpack-dev-server

this is my jsx:
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h1>hello world</h1>,
document.getElementById('content')
)
I build with webpack to build/bundle.js &
I import bundle.js into index.html
index.html:
<!DOCTYPE html>
<html>
<head>
┊ <meta charset="utf-8">
┊ <meta name="viewport" content="width=device-width">
┊ <title></title>
</head>
<body>
┊ <div id="content"></div>
┊ <script src="./build/bundle.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
But when I ran webpack-dev-server, chrome console error:
bundle.js:57 Uncaught ReferenceError: React is not defined
I am sure, I have run npm install react
how can I fix it?(-_-)ゞ゛
You should add this into your app.js not .jsx
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h1>hello world</h1>,
document.getElementById('content')
)
JSX is a transpiled language, interpreted by React components. So, there has to be React in app.js for React components to interpret the jsx!!
JSX syntax and ES6, are not supported in all the browsers.
Hence, if we are using them in the React code, we need to use a tool which translates them to the format that has been supported by the browsers. Babel is one such tool
Webpack uses loaders to translate the file before bundling them
To setup, install the following npm packages
npm i babel-loader babel-preset-es2015 babel-preset-react -S
The babel-preset-es2015 and babel-preset-react are plugins being used by the babel-loader to translate ES6 and JSX syntax respectively.
The next step is telling Webpack to use the babel-loader while bundling the files
// Existing Code ....
var config = {
// Existing Code ....
module : {
loaders : [
{
test : /\.jsx?/,
include : APP_DIR,
loader : 'babel'
}
]
}
}
The loaders property takes an array of loaders,below I have used just babel-loader. Each loader property should specify what are the file extension it has to process via the test property. .js and .jsx files can be configured using the regular expression as /\.jsx?/
Final webpack.config.js
var webpack = require('webpack');
var path = require('path');
module.exports = {
context: path.join(__dirname, "src"),
devtool: "inline-sourcemap",
entry: "./js/client.js",
module: {
loaders: [
{
test: /\.jsx?/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0'],
plugins: ['react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy'],
}
}
]
},
output: {
path: __dirname + "/src/",
filename: "client.min.js"
},
plugins: [
new webpack.ProvidePlugin({
'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
})
]
};

Resources