Rendering UI in React Router 4 - reactjs

I am still fairly new to ReactJS especially the routing portion of it. I have a couple of questions which I tried many solutions i found on stackoverflow but nothing seems to be helping me:
When i start my react project locally, i get localhost:3000. What if I want it to be shown as localhost:3000/extension/?
When i navigate about my project i can render the different pages. However, if i enter the URL directly into the browser, a blank page is rendered. There is no error. I read somewhere saying that my components are not connected to my routes. I am not sure what I need to do to correct it.
How can i ensure history is properly being utilized?
routes.js:
const RouteList = () => (
<Switch>
<Route path="/extension/" exact component={withRouter(HomePage)} />
<Route path="/extension/something" exact component={withRouter(SomethingPage)} />
<Route component={Error} />
</Switch>
);
App.js:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render () {
return (
<Router history={browserHistory}>
<div>
<Header />
<RouteList />
<Footer />
</div>
</Router>
);
}
}
export default App;
--EDIT--
Added my webpack.config.js
const commonConfig = {
resolve: {
modules: [path.resolve('./src'), 'node_modules'],
extensions: ['.js', '.csv', '.json', '.scss', '.css', '.html']
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
enforce: 'pre',
use: [{ loader: 'eslint-loader', options: { configFile: '.eslintrc' } }]
},
{
use: 'babel-loader',
test: /\.js$/,
exclude: /node_modules/
},
{
test: /\.html$/,
use: [{ loader: 'htmlhint-loader', options: { configFile: '.htmlhintrc' } }],
exclude: /node_modules/,
enforce: 'pre'
},
{
test: /\.(png|jpg|jpeg|svg|gif|svg|woff|woff2|ttf|eot)(\? v=\d+\.\d+\.\d+)?$/,
use: 'file-loader'
},
{
use: [{
loader: 'html-loader'
}],
test: /\.html$/
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(nodeEnv)
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module) {
if (module.resource && (/^.*\.
(css|less|scss)$/).test(module.resource)) {
return false;
}
return module.context && module.context.indexOf('node_modules') !== -1;
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
new CopyWebpackPlugin([{
from: __dirname + '/src/images',
to: ''
}]),
new HtmlWebpackPlugin({
template: 'src/index.html',
chunksSortMode: 'dependency'
}),
new webpack.optimize.ModuleConcatenationPlugin()
]
};
const devConfig = {
entry: {
main: ['whatwg-fetch', 'core-js/es6', 'react-hot-loader/patch', 'index.js',
'webpack-hot-middleware/client?reload=true']
},
target: 'web',
devtool: 'inline-source-map',
output: {
path: path.join(__dirname, '/dev_build'),
filename: '[name].bundle.js',
publicPath: '/'
},
module: {
rules: [
{
test: /\.scss/,
include: path.resolve(__dirname, 'src/styles'),
use: ['style-loader', 'css-loader', {loader: 'sass-loader', options:
{sourceMap: true}}]
},
{
test: /\.css$/,
exclude: [/node_modules/],
use: ['style-loader', 'css-loader?modules']
},
{
test: /\.css$/,
include: [/node_modules/],
use: ['style-loader', 'css-loader']
}
]
},
devServer: {
contentBase: 'src',
compress: true,
hot: true,
port: 3000,
host: '0.0.0.0',
disableHostCheck: true,
historyApiFallback: {
disableDotRule: true,
index: 'build/index.html'
},
stats: 'minimal',
overlay: true,
proxy: {
'/api/**': {
target: {
port: 8080
},
secure: false
},
'/actuator/**': {
target: {
port: 8080
},
secure: false
},
}
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.NoEmitOnErrorsPlugin()
]
};
Please help cause I am not sure what I am doing wrong and i am splitting my brains over this. Thanks.

You can add a Redirect from / to /extension if you want that to be the landing page.
const RouteList = () => (
<Switch>
<Redirect from='/' to='/extension/'/>
<Route path="/extension/" exact component={HomePage} />
<Route path="/extension/something" exact component={SomethingPage} />
<Route component={Error} />
</Switch>
);
Your app currently only works when visited on / because your server will only serve index.html for the index route. You can use historyApiFallback if you want the index.html file to be served instead of any 404 responses.
webpack.config.js
module.exports = {
//...
devServer: {
historyApiFallback: true
}
};

Related

Blocked due to MIME type mismatch (X-Content-Type-Options: nosniff)

I'm new to WebPack, and it's hard to debug it and I can't seem to find any right answer on google.
I'm trying to use react router, but it seems like it needs a lot of configuration to make it work exactly like create-react-app's.
React router sub-routes seems to work only when you redirect it with the Link component, example:
// App.jsx
<Route path="/profile/me/" element={<PrivateRoute component={Profile} />}>
<Route path="create" element={<PrivateRoute component={CreateProfile} />} />
</Route>;
// Sub-Component - Profile.jsx
<Link to="create">
<Button color="primary" variant="contained">
Set It Up
</Button>
</Link>;
But when I try to enter the URL in the browser search bar, I get an error:
GET
http://localhost:3000/profile/dist/bundle.js [HTTP/1.1 404 Not Found 4ms]
The resource from “http://localhost:3000/profile/dist/bundle.js” was blocked due to MIME type mismatch (X-Content-Type-Options: nosniff).
create
Here's my Webpack config:
const path = require("path");
const webpack = require("webpack");
const Dotenv = require('dotenv-webpack');
module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules)/,
loader: "babel-loader",
options: {
presets: ["#babel/env"],
},
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.js$/,
include: /node_modules\/react-dom/,
use: ["react-hot-loader/webpack"],
},
],
},
resolve: {
extensions: ["*", ".js", ".jsx"],
},
output: {
path: path.resolve(__dirname, "dist/"),
publicPath: "/dist/",
publicPath: "/profile/dist/",
filename: "bundle.js",
},
devServer: {
// contentBase
static: {
directory: path.join(__dirname, "public/"),
},
port: 3000,
// publicPath
devMiddleware: {
publicPath: "https://localhost:3000/dist/",
},
// hotOnly
hot: "only",
// To use router
historyApiFallback: true,
// Proxy
/*proxy: {
"/api/v1": "http://localhost:8000",
},*/
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new Dotenv({
systemvars: true,
}),
],
};

React router with basename and webpack initial url

Hello i am stuck with this one and couldnt figure it out. So i have React router v6 with basename - "/ui/" , and i want that when i open localhost:8080 that it automatically turns into "localhost:8080/ui/" because the real view is actually rendered in /ui/ route.
Router setup:
<BrowserRouter basename="/ui/">
...routes here
</BrowserRouter>
Webpack dev:
module.exports = {
mode: 'development',
devtool: 'cheap-module-source-map',
devServer: {
hot: true,
historyApiFallback: true,
open: true,
port: 6969
}
}
And common webpack:
module.exports = {
entry: path.resolve(__dirname, '..', './src/index.tsx'),
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
},
],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader"
]
},
{
test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource',
},
{
test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
type: 'asset/inline',
},
],
},
output: {
path: path.resolve(__dirname, '..', './build'),
filename: 'bundle.js',
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '..', './src/public/ui/index.html'),
}),
],
stats: 'errors-only',
}
I have tried to add to output publicPath: '/ui/' but it didnt work.
Or even something like this:
static: [
{
publicPath: '/',
directory: path.join(__dirname, "../public/ui/"),
}]
How i can achieve this withouth "redirecting" :\
Try this solution
<Route exact path="/">
<Redirect to="/home" />
</Route>
Check your main.js or similar in your src or sources.
Mine had:
const Root = () => (
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
{appRoute}
</Route>
</Router>
</Provider>
);
and so just had to move it to Route path="/ui/" component={App}

React webpack showing blank page on refresh

I am serving my static files from express and i can see that it loads index.html
But the problem is when on a rout for example localhost:8080/users and i refresh the page, the .css and main.js does not seam to be loaded so it returns blank page.. Any idea on what night be the problem?
My app.js
function App (){
return (
<div className="wrapper">
<Routes>
{routes.map(({ path, name, Component }, key) => (
<Route exact path={path} key={key} element={Component} />
))}
</Routes>
</div>
);
}
export default App
My index.js:
ReactDOM.render(
<Provider store={store}>
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="*" element={<App />} />
</Routes>
</BrowserRouter>
</React.StrictMode>
</Provider>,
document.getElementById('root')
);
This is my webpack:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const Dotenv = require('dotenv-webpack');
const webpack = require('webpack');
require('dotenv').config({path:__dirname+'/../.env'})
module.exports = {
entry: {
app: path.join(__dirname, "/src/Index.js"),
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "main.js",
},
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
exclude: /node_modules/,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
{
test: /\.css$/,
use: 'style-loader!css-loader'
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: "babel-loader",
},
{
test: /\.html$/,
exclude: /node_modules/,
use: "html-loader",
},
],
},
resolve: {
extensions: [".js", ".jsx"],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
APP_API_URL : JSON.stringify(process.env.APP_API_URL)
},
}),
new Dotenv(),
new HtmlWebpackPlugin({
filename: "index.html",
template: "./src/index.html",
}),
new MiniCssExtractPlugin({
filename: "app.css",
chunkFilename: "[id].css",
}),
],
devServer: {
historyApiFallback: true,
contentBase: path.join(__dirname, "dist"),
compress: true,
port: process.env.PORT_CLIENT,
open: true,
stats: "errors-only",
}
};
Check out this finished example using React Router and compare it with your App.
https://stackblitz.com/edit/github-agqlf5?file=src%2Fmain.jsx

CSS Modules export CSSes as <style> tag in head tag with disabled sourceMap

I'm writing a React, Server Side Rendering, Router4, Helmet, CSS Modules boilerplate, everything is awesome but one thing hurts my soul:
At first see my webpack.production.config.js:
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const StatsPlugin = require('stats-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const distDir = path.join(__dirname, 'dist');
const srcDir = path.join(__dirname);
module.exports = [
{
name: 'client',
target: 'web',
entry: `${srcDir}/client.jsx`,
output: {
path: distDir,
filename: 'client.js',
publicPath: distDir,
},
resolve: {
extensions: ['.js', '.jsx']
},
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: '[hash:base64:10]',
sourceMap: false,
}
},
{
loader: 'sass-loader'
}
]
}
],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new CleanWebpackPlugin(distDir),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
screw_ie8: true,
drop_console: true,
drop_debugger: true
}
}),
new webpack.optimize.OccurrenceOrderPlugin(),
]
},
{
name: 'server',
target: 'node',
entry: `${srcDir}/server.jsx`,
output: {
path: distDir,
filename: 'server.js',
libraryTarget: 'commonjs2',
publicPath: distDir,
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules\/)/,
use: [
{
loader: 'babel-loader',
}
]
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "isomorphic-style-loader",
use: [
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[hash:base64:10]',
sourceMap: false
}
},
{
loader: 'sass-loader'
}
]
})
}
],
},
plugins: [
new ExtractTextPlugin({
filename: 'styles.css',
allChunks: true
}),
new OptimizeCssAssetsPlugin({
cssProcessorOptions: { discardComments: { removeAll: true } }
}),
new StatsPlugin('stats.json', {
chunkModules: true,
modules: true,
chunks: true,
exclude: [/node_modules[\\\/]react/],
}),
]
}
];
And this is my template.js file that server use this for building basic HTML:
export default ({ markup, helmet }) => {
return `<!DOCTYPE html>
<html ${helmet.htmlAttributes.toString()}>
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
</head>
<body ${helmet.bodyAttributes.toString()}>
<div id="root">${markup}</div>
<script src="/dist/client.js" async></script>
</body>
</html>`;
};
Just like I write, I set sourceMap to false, so all styles loaded as <style> tag inside <head> tag.
If I set the soureMap to true, the <link> tag appears inside <head> tag, Even it is not server side, and has a weird blob url for loading CSS:
In fact I wanna server side link tag inside head tag with direct pointing to styles.css, how I can do it ?
My whole project is in THIS LINK
It has not very lot codes, little and simple, it is just a simple boilerplate. take a look
For production builds,inside client config, you don't use the style loader. You need to use the extract-text-webpack-plugin instead. You did it correctly in your server build config. But this config shouldn't be in your server build, as in the server's source code, you never use (s)css files.
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[hash:base64:10]',
}
},
{
loader: 'sass-loader'
}
]
})
}
...
plugins: [
new ExtractTextPlugin({
filename: 'styles.css'
}),
]
Add this to your client build config.
link={[{rel: "stylesheet", href: "/dist/styles.css"}]}
And add a link props to your App.jsx to load a <link> tag inside your <head> tag.
So your App.jsx render methode became:
render() {
return (
<div>
<Helmet
htmlAttributes={{lang: "en", amp: undefined}} // amp takes no value
titleTemplate="%s | React App"
titleAttributes={{itemprop: "name", lang: "en"}}
meta={[
{name: "description", content: "Server side rendering example"},
{name: "viewport", content: "width=device-width, initial-scale=1"},
]}
link={[{rel: "stylesheet", href: "/dist/styles.css"}]}/*ADD THIS*/
/>
<Switch>
<Route exact path='/' component={Homepage}/>
<Route path="/about" component={About}/>
<Route path="/contact" component={Contact}/>
</Switch>
</div>
);
}
Your client build config builds/bundles everything need for frontend. JS, CSS, Images, ... and puts this to dist folder.
Your server only serves this dist folder from root /. That's the only thing your server does (besides providing an api for example)

Routes chunks are bundling external scripts in every chunk

In my webpack I've used externals which has React, React Dom, Redux etc.
Now when I implement my Route Chunking, every chunk which is generated re-bundles the external scripts again, so eventually my bundle size is very huge.
How can I avoid my individual chunks not to re-bundle the external scripts and use them from externals.
EDIT
Using https://chrisbateman.github.io/webpack-visualizer/ I can see that all my chunks are bundling common libs - which are are actually supposed to come from externals in webpack.
EDIT 2
webpack file
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: ['./src/containers/AppContainer', './src/index'],
devtool: 'cheap-module-source-map',
output: {
path: __dirname + '/dist',
publicPath: 'public/',
filename: 'bundle.js',
chunkFilename: '[name].[id].chunk.[chunkhash].js',
libraryTarget: 'umd'
},
target: 'web',
externals: {
antd: 'antd',
react: 'react',
'react-dom': 'react-dom',
'react-router': 'react-router',
redux: 'redux',
'react-redux': 'react-redux',
immutable: 'immutable',
},
resolve: {
modules: [
path.join(__dirname, '../node_modules')
],
extensions: ['.js', '.jsx', '.json'],
alias:{
constants: path.resolve(__dirname, './src/constants'),
actions: path.resolve(__dirname, './src/actions'),
styles: path.resolve(__dirname, './src/styles'),
utils: path.resolve(__dirname, './src/utils')
}
},
resolveLoader: {
modules: [
path.join(__dirname, '../node_modules')
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
comments: false
})
]
module: {
loaders: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// Ignore local .babelrc files
babelrc: false,
presets: [
['es2015', { modules: false }],
'react'
],
plugins: [
'react-html-attrs',
'transform-class-properties',
'transform-decorators-legacy',
'transform-object-rest-spread',
[
'import', {
libraryName: 'antd'
}
]
]
}
},
{ test: /\.png$/, loader: 'file-loader' },
{
test: /\.s?css$/i,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.s?less$/i,
exclude:'/node_modules/',
use: [
'style-loader',
'css-loader',
'less-loader'
]
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader',
options: {
limit: 100000
}
},
{
test: /\.eot\?iefix$/,
loader: 'url-loader',
options: {
limit: 100000
}
},
{
enforce: 'pre',
test: /\.js$/,
loader: 'eslint-loader',
exclude: /node_modules/,
options: {
configFile: './eslint/.eslintrc',
failOnWarning: false,
failOnError: false
}
}
]
}
};
Routes file
import React from 'react';
import { Route, IndexRoute } from 'react-router';
export default (
<Route path='/base/'
getComponent={ (location, callback) => {
require.ensure([], function (require) {
callback(null, require('./containers/AppContainer').default);
});
} }>
<Route path='/route1'
getComponent={ (location, callback) => {
require.ensure([], function (require) {
callback(null,
require('./containter1')
.default);
});
} }
/>
<Route path='/route2'
getComponent={ (location, callback) => {
require.ensure([], function (require) {
callback(null,
require('./container2')
.default);
});
} }
/>
<Route path='/route3'
getComponent={ (location, callback) => {
require.ensure([], function (require) {
callback(null,
require('./container3')
.default);
});
} }
/>
</Route>
);
Try to change your externals section like this:
externals: {
React: require.resolve('react'),
'window.React': require.resolve('react'),
ReactDOM: require.resolve('react-dom'),
'window.ReactDOM': require.resolve('react-dom')
}
Also, remove import React from 'react' from your code. Just use React.
EDIT
Apologies, I edited my answer. Just realised my mistake. I changed the code. The key inside your externals will be the name of the global variable. Usually it's also safer to put it inside the window object

Resources