React router v4 broswer history not working with code splitting - javascript

When using hash history code splitting works with react router but now i'm about to go into production and i want to switch to browser-history it gives an error when i try to change route, example if i try going to the login route 127.0.0.1:8080/auth/login :
Refused to execute script from
'http://127.0.0.1:8080/auth/3.bundle.js' because its MIME type
('text/html') is not executable, and strict MIME type checking is
enabled.
and
Uncaught (in promise) Error: Loading chunk 3 failed. (error:
http://127.0.0.1:8080/auth/3.bundle.js)
at HTMLScriptElement.onScriptComplete (bootstrap:108)
This is my router
<Router history={history}>
<ConnectApp />
</Router>
Connect app:
<Switch>
{/* Front end */}
<Route path="/" component={AsyncHome} exact />
<Route path="/post/:id" component={AsyncPost} exact />
{/* authentication */}
<Route path="/auth/:section" component={AsyncLogin} exact />
{/* Backend */}
<PrivateRoute path="/admin/:path" component={AsyncAdmin} exact />
<PrivateRoute
path="/admin/edit-post/:id"
component={AsyncEditPost}
exact
/>
<Route component={Err404} />
</Switch>
history.js:
import { createBrowserHistory } from 'history';
export default createBrowserHistory({
// add configurations here
});
webpack.dev.config.js
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
historyApiFallback: true
},
plugins: [
new BundleAnalyzerPlugin()
]
}
*If there is any more code to add please indicate in the comment
Thank you

Add publicPath:"/" to the config:
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
historyApiFallback: true
},
plugins: [
new BundleAnalyzerPlugin()
],
output: {
path: path.resolve('dist'),
filename: '[name].bundle.js',
chunkFilename: '[name].bundle.js',
publicPath: '/' // Add this line
},
}

Related

/class/:id page doesn't show in React app

I am developing React app and I have some problems while implementing routing.
<BrowserRouter>
<Layout>
<Route path="/" component={Home} exact={true} />
<Route path="/signin" component={Signin} />
<Route path="/register" component={Register} />
<Route path="/about" component={About} />
<Route path="/class/:id" component={Class} />
<Route path="/class" component={Classes} exact={true} />
</Layout>
</BrowserRouter>
When I navigate home page on local , i.e localhost:8000 , home page is displayed.
And when I click class link, browser navigates to /class and classes page is displayed.
And when I click specific class item , browser navigate to /class/:class-id and specific class detail page is displayed.
But when I manually type in /class/:class-id in browser, class detail page is not displayed.
When I inspect the browser, bundle.js was referred to localhost:8000/class/bundle.js.
But bundle.js is referred to localhost:8000/bundle.js.
I am using webpack and here's webpack configuration.
const HtmlWebpackPlugin = require('html-webpack-plugin');
const InterpolateHtmlPlugin = require('interpolate-html-plugin');
const path = require('path');
module.exports = {
mode: 'development',
entry: "./src/index.js",
// Here the application starts executing
// and webpack starts bundling
output: {
// options related to how webpack emits results
path: path.resolve(__dirname, "dist"), // string
// the target directory for all output files
// must be an absolute path (use the Node.js path module)
filename: "bundle.js", // string,
// the filename template for entry chunks,
assetModuleFilename: "assets/[hash][ext][query]"
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader'
},
{
test: /\.less$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" },
{ loader: "less-loader" }
]
},
{
test: /\.(jpe?g|png|gif|svg)$/,
type: "asset/resource"
}
]
},
resolve: {
extensions: ['.js', '.jsx'],
alias: {
'#': path.resolve(__dirname, 'src/'),
}
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
favicon: './public/favicon.ico',
}),
new InterpolateHtmlPlugin({
PUBLIC_URL: 'http://localhost:8000/public'
})
],
devServer: {
historyApiFallback: true,
port: 8000,
open: true
},
externals: {
// global app config object
config: JSON.stringify({
apiUrl: 'http://localhost:4000'
})
}
}
I found a simple solution.
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
favicon: './public/favicon.ico',
publicPath: '/'
}),
new InterpolateHtmlPlugin({
PUBLIC_URL: 'http://localhost:8000/public'
})
]
I added publicPath: '/' to HtmlWebpackPlugin so that bundle.js can be referred to absolute path, not relative path. localhost:8000/bundle.js
So it works perfect now.
On the other hand, I researched isomorphic application but found that its main benefits are to improve performance & SEO implementation.
This idea appeared to improve performance & SEO implementation after modern JS frameworks which have client rendering ability.
Since Google announced and supported SEO regarding javascript, this idea lost enthusiast , but still be used for its benefits.
I am sure isomorphic and catch-all method can resolve my issue with perfect.
But for now, I prefer my current solution, it's simple.

react-router-dom error when refreshing page with id

I'm developing a project using React/Redux and Node.js.
I added react-router-dom as a dependency into my project and configured the router like this:
import ...
const Router = () => (
<main>
<Switch>
<Route exact path='/' component={components.main}/>
<Route path='/caseDetail/:id' component={components.caseDetail}/>
<Route component={components.notFound}/>
</Switch>
</main>
)
export default Router
And setup my webpack like this
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const merge = require('webpack-merge')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const env = require('./src/env.js')
const TARGET = process.env.npm_lifecycle_event
process.env.BABEL_ENV = TARGET
const PATHS = {
app: path.join(__dirname, 'src'),
build: path.join(__dirname, 'build'),
assets: path.join(__dirname, 'assets')
}
const common = {
entry: [PATHS.app],
module: {
rules: [
{
test: /\.json$/,
loader: 'json-loader'
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
include: PATHS.app,
loaders: ['babel-loader']
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { modules: true } }],
include: /flexboxgrid/
},
{
test: /\.scss$/,
exclude: /flexboxgrid/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { modules: true } },
'sass-loader'
]
},
{
test: /\.jpe?g$|\.gif$|\.png$/i,
loader: 'url-loader?name=[name].[ext]'
},
{
test: /\.otf$|\.eot$|\.svg$|\.ttf|\.woff|\.woff2$/,
loader: 'url-loader?name=[name].[ext]'
}
]
},
resolve: {
extensions: ['.js', '.jsx'],
modules: ['node_modules', path.resolve(__dirname, './node_modules')],
mainFields: ['browser', 'web', 'browserify', 'main', 'style']
}
}
if (TARGET === 'start' || !TARGET) {
module.exports = merge(common, {
module: {
rules: [
{
test: /\.jsx?$/,
use: ['source-map-loader'],
enforce: 'pre'
}
]
},
devtool: 'inline-source-map',
devServer: {
contentBase: PATHS.build,
historyApiFallback: true,
hot: true,
inline: true,
progress: true,
stats: 'errors-only',
https: true,
host: env.host,
port: env.port,
overlay: {
errors: true
},
watchOptions: {
watch: true
}
},
plugins: [
new MiniCssExtractPlugin({ filename: 'assets/style.css' }),
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
template: PATHS.app + '/index.html',
inject: 'body'
})
],
output: {
path: PATHS.build,
filename: 'bundle.js'
}
})
}
if (TARGET === 'production') {
module.exports = merge(common, {
plugins: [
new MiniCssExtractPlugin({ filename: 'style.css' }),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: "'production'"
}
})
],
output: {
path: '/build',
filename: 'bundle.js'
}
})
}
My problem is
When I call this route /caseDetail/1 it works by using Link:
<Link to={{ pathname: `/caseDetail/` + caseStudy.id }}>
BUT, if I call directly by the browser, like https://localhost:3000/caseDetail/1
It fails, I get this message on console
Refused to apply style from 'https://localhost:3000/caseDetail/assets/style.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
0:12 GET https://localhost:3000/caseDetail/bundle.js net::ERR_ABORTED 404
0:1 Refused to execute script from 'https://localhost:3000/caseDetail/bundle.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
SOLVED
How to fix. Thanks to #soroush-chehresa
I added into my router file, the BrowserRouter like this:
import React from 'react'
import { Switch, Route, BrowserRouter } from 'react-router-dom'
import { components } from './loader'
const Router = () => (
<main>
<BrowserRouter>
<Switch>
<Route exact path='/' component={components.main}/>
<Route exact path='/caseDetail' component={components.caseDetail}/>
<Route path='/caseDetail/:id' component={components.caseDetail}/>
<Route component={components.notFound}/>
</Switch>
</BrowserRouter>
</main>
)
export default Router
And also added into my webpack.config.js on output
publicPath: '/'
If you are using react-router-redux wrap Router with ConnectedRouter:
import { ConnectedRouter } from 'react-router-redux';
import { createBrowserHistory } from 'history';
const Router = () => (
<main>
<ConnectedRouter history={createBrowserHistory()}>
<Switch>
<Route exact path='/' component={components.main}/>
<Route path='/caseDetail/:id' component= {components.caseDetail}/>
<Route component={components.notFound}/>
</Switch>
</ConnectedRouter>
</main>
);
Otherwise if you are using react-router-dom wrap Router with BrowserRouter:
import { BrowserRouter } from 'react-router-dom';
const Router = () => (
<main>
<BrowserRouter>
<Switch>
<Route exact path='/' component={components.main}/>
<Route path='/caseDetail/:id' component={components.caseDetail}/>
<Route component={components.notFound}/>
</Switch>
</BrowserRouter>
</main>
);

Routes do not show when typing sub-url in the url browser field

I'm not able to navigate to other routes in my react app.
My home route is visible and works when I start my app locally (npm run dev).
I don't get any errors in the console.
Here is my Main router component code:
import React from 'react'
import { Switch, Route, Router } from 'react-router-dom'
import Layout from './components/Layout.js'
import Statistic from './components/statisticscomponent/Statistic.js'
import Starter from './components/starter/Starter.js'
export const Main = () => (
<Switch>
<Route exact path="/" component={Layout}/>
<Route path="/abc" component={Statistic}/>
<Route path="/edf" component={Starter}/>
</Switch>
)
This is the main script.js which launches the app:
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<Main/>
</BrowserRouter>
</Provider>, app);
If you need more code, please post in the comment section.
As I've said, webpack.config.js file had to be modified a little bit in order for this to work.
Inside your webpack.config.js write this block of code:
devServer: {
historyApiFallback: true,
contentBase: './',
hot: true
},
EXTENDED
I have put the code inside
module.exports = {...}
after my output block of code:
output: {
path: __dirname + "/js",
filename: "scripts.min.js",
},
devServer: {
historyApiFallback: true,
contentBase: './',
hot: true
},

React JS - React-Router nested route

I'm new on React and I try to build my first application, for the route I used react-router and this is my App
class App extends Component {
render() {
return (
<Router history={browserHistory}>
<Route path='/' component={Container}>
<IndexRoute component={Home}/>
<Route path='/address' component={Address}>
<IndexRoute component={TwitterFeed} />
<Route path='instagram' component={Instagram} />
<Route path='query' component={Query} />
</Route>
<Route path='/about/:name' component={About} />
<Route path='/namedComponent' component={NamedComponents}>
<IndexRoute components={{ title: Title, subTitle: SubTitle }} />
</Route>
<Route path='*' component={NotFound} />
</Route>
</Router>
)
}
}
Everything work well but not this line <Route path='/about/:name' component={About} />, in fact if i try to write /address/12345 white page appears to me whitout error, someone can help me?
This is my webpack.config.js
'use strict';
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var webpack = require('webpack');
module.exports = {
entry: {
app: [
'./src/main.js'
]
},
output: {
path: __dirname + '/dist',
publicPath : '/',
filename: 'bundle.js'
},
plugins: process.env.NODE_ENV === 'production' ? [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin({ filename: '[name].css', disable: false, allChunks: true})
] : [],
devServer: {
contentBase: "./dist",
inline: true,
port: 3000,
},
//source map for js
devtool: 'source-map',
module: {
loaders: [
//babel ECMAScript 6 compiler
{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.scss$/,
loaders: [ 'style-loader', 'css-loader?sourceMap', 'sass-loader?sourceMap' ] //sourceMap for debug css in devTools
/*loader: ExtractTextPlugin.extract({
fallback : 'style-loader', use : 'css-loader?sourceMap!sass-loader?sourceMap'
}) FOR PROD*/
}
]
},
}
And i use this script : webpack-dev-server --inline --content-base dist --history-api-fallback
In your component "Container" you should have children object.
You can have a better look on the snippet bellow.
import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
const BasicExample = () =>
<Router>
<div>
<ul>
<Route to="/" component={ComponentThatHaveChildren} />
<Route path="/path1/path2" component={ChildrenComponent} />
</ul>
</div>
</Router>;
const ComponentThatHaveChildren = props =>
<div>
<h1>Component That Have Children</h1>
<h2>{props.children}</h2>
</div>;
const ChildrenComponent = () =>
<div>
<h2>Children</h2>
</div>;
export default BasicExample;

React Router 4 points to primary domain not subfolder

I have an app which is within a sub folder of my primary domain. So Test.com is my domain and my app is in a folder called 'Player' in that domain i.e Test.com/player.
The problem is React Router points to Test.com when it needs to point to Test.com/player.
The other issue is my index file is in a folder called 'public'.
How do I do this in React Router 4/htacess?
Thanks
App
import React from "react";
import {render} from "react-dom";
import {BrowserRouter, Route, Switch, hashHistory} from "react-router-dom";
import Nav from "./components/Nav";
import Home from "./components/Home";
import About from "./components/About";
import Contact from "./components/Contact";
class App extends React.Component {
render() {
return (
<BrowserRouter history={hashHistory}>
<div>
<Nav />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
<Route render={() => {
return <h1>Not Found!</h1>
}} />
</Switch>
</div>
</BrowserRouter>
);
}
}
render(<App />, document.getElementById("main"));
Webpack
const webpack = require("webpack");
module.exports = {
entry: {
filename: "./app/app.js"
},
output: {
filename: "./app/build/bundle.js"
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
query: {
presets: ["es2015", "react"]
}
}
]
},
devServer: {
inline: true,
port: 5000,
contentBase: "./public",
historyApiFallback: true,
}
}
According to React Router Docs, BrowserRouter has
basename: string The base URL for all locations. If your app is served
from a sub-directory on your server, you’ll want to set this to the
sub-directory. A properly formatted basename should have a leading
slash, but no trailing slash.
you can use
class App extends React.Component {
render() {
return (
<BrowserRouter history={hashHistory} basename="/player">
<div>
<Nav />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
<Route render={() => {
return <h1>Not Found!</h1>
}} />
</Switch>
</div>
</BrowserRouter>
);
}
}
You also need those properties on your webpack.config.js
module.exports = {
entry : 'your entry',
output : {
path : 'your path',
filename : 'your filename',
publicPath : '/' //ADD THIS LINE (either / or /player will work)
},
devServer : {
historyApiFallback : true // ADD THIS LINE
}
}

Categories