Can't load JS bundle on route in React - reactjs

I have a React app, using webpack. I have added React Router 3 and when trying to visit a route in my app, I get a 404 on the .JS files when on anything but the initial route.
My application is also using a basename as I must prepend the application path.
Routes
import React from 'react'
import Loadable from 'react-loadable'
import { Router, Route, IndexRoute, IndexRedirect } from 'react-router'
import App from './components/App'
const AsyncRoute = loader =>
Loadable({
loader,
loading: () => <h3>Loading...</h3>,
delay: 300,
})
const LandingPage = AsyncRoute(() => import(/* webpackPrefetch: true, webpackChunkName: "landingPage" */ './containers/LandingPage'))
export default ({ history }) => (
<Router history={history}>
<Route path="/:tenant" component={App}>
<IndexRoute component={LandingPage} />
<Route path="foo" component={LandingPage} />
</Route>
</Router>
)
Index
import React, { Fragment } from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { useRouterHistory } from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'
import createBrowserHistory from 'history/lib/createBrowserHistory'
import store from './store'
import Router from './routes'
const historyConfig = { basename: '/content-manager' }
const browserHistory = useRouterHistory(createBrowserHistory)(historyConfig)
const history = syncHistoryWithStore(browserHistory, store)
render(
<Provider store={store}>
<Fragment>
<Router history={history} />
</Fragment>
</Provider>,
document.getElementById('root')
)
Webpack.dev
const PORT = process.env.SERVER_PORT || 3000
const HOST = process.env.SERVER_HOST || '127.0.0.1'
module.exports = {
devtool: 'inline-source-map',
mode: 'development',
output: {
filename: '[name].[hash].js',
publicPath: '',
},
devServer: {
host: 'localhost',
port: process.env.PORT || 3200,
historyApiFallback: true,
hot: false,
open: true,
proxy: {
'/api/**': {
target: `http://${HOST}:${PORT}`,
pathRewrite: { '^/api': '' },
secure: false,
logLevel: 'debug',
},
},
},
}
Webpack.common
const commonPaths = require('../common-paths');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const Dotenv = require('dotenv-webpack');
module.exports = {
context: commonPaths.appPath,
entry: ['babel-polyfill', './index.jsx'],
resolve: {
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{ test: /\.(jsx?)$/, exclude: /node_modules/, use: ['babel-loader'] },
],
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
chunks: 'initial',
test: 'vendor',
name: 'vendor',
enforce: true,
},
},
},
},
plugins: [
new HtmlWebpackPlugin({
title: 'Web App',
template: commonPaths.projectRoot + '/public/index.html',
inject: 'body',
}),
new CompressionPlugin({
algorithm: 'gzip',
test: /\.js$|\.css$/,
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: process.env.NODE_ENV === 'prod',
}),
new Dotenv(),
],
};
If I visit http://app.local/content-manager/foo in the browser, the landing page will load.
If I then trying and navigate to http://app.local/content-manager/foo/edit the application does not load and I see the following in the console
GET http://app.local/content-manager/foo/main.fdb34ff55bc02a8cd347.js 404 (Not Found)
edit:1 Refused to execute script from 'http://app.local/content-manager/foo/main.fdb34ff55bc02a8cd347.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
I believe on the initial route, it is trying to find the js bundle at http://app.local/content-manager but then when I move to a sub route it is looking in http://app.local/content-manager/foo

So I have added
<base href="/content-manager/" />
into my index.html and now it seems to work....

Related

isomorphic-style-loader doesn't work as it supposed to

Hey I am doing this simple react + SSR project that incorporates the isomorphic-style loader. I followed the step-by-step guide to implement it as detailed here https://www.npmjs.com/package/isomorphic-style-loader but it just doesn't work. The style I made is not showing. Can anyone guide me in fixing this issue?
Here is my webpack config
var path = require('path');
var webpack = require('webpack');
var nodeExternals = require('webpack-node-externals');
var browserConfig = {
entry: './src/browser/index.js',
output: {
path: path.resolve(__dirname, 'public'),
filename: 'bundle.js',
publicPath: '/',
},
module: {
rules: [
{ test: /\.(js)$/, use: 'babel-loader' },
{
test: /\.css$/,
use: [
'isomorphic-style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
'postcss-loader',
],
},
],
},
mode: 'production',
plugins: [
new webpack.DefinePlugin({
__isBrowser__: 'true',
}),
],
};
var serverConfig = {
entry: './src/server/index.js',
target: 'node',
externals: [nodeExternals()],
output: {
path: __dirname,
filename: 'server.js',
publicPath: '/',
},
mode: 'production',
module: {
rules: [
{ test: /\.(js)$/, use: 'babel-loader' },
{
test: /\.css$/,
use: [
'isomorphic-style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
'postcss-loader',
],
},
],
},
plugins: [
new webpack.DefinePlugin({
__isBrowser__: 'false',
}),
],
};
module.exports = [browserConfig, serverConfig];
here is my index.js (server)
import express from 'express';
import cors from 'cors';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter, matchPath } from 'react-router-dom';
import serialize from 'serialize-javascript';
import StyleContext from 'isomorphic-style-loader/StyleContext';
import App from '../shared/App';
import routes from '../shared/routes';
const app = express();
app.use(cors());
app.use(express.static('public'));
app.get('*', (req, res, next) => {
const css = new Set(); // CSS for all rendered React components
const insertCss = (...styles) =>
styles.forEach((style) => css.add(style._getCss()));
const activeRoute = routes.find((route) => matchPath(req.url, route)) || {};
const promise = activeRoute.fetchInitialData
? activeRoute.fetchInitialData(req.path)
: Promise.resolve();
promise
.then((data) => {
const context = { data };
const markup = renderToString(
<StyleContext.Provider value={{ insertCss }}>
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
</StyleContext.Provider>
);
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>SSR with RR</title>
<script src="/bundle.js" defer></script>
<script>window.__INITIAL_DATA__ = ${serialize(data)}</script>
<style type="text/css">${[...css].join('')}</style>
</head>
<body>
<div id="app">${markup}</div>
</body>
</html>
`);
})
.catch(next);
});
app.listen(3000, () => {
console.log(`Server is listening on port: 3000`);
});
here is my index.js (browser)
import React from 'react';
import { hydrate } from 'react-dom';
import App from '../shared/App';
import { BrowserRouter } from 'react-router-dom';
import StyleContext from 'isomorphic-style-loader/StyleContext';
const insertCss = (...styles) => {
const removeCss = styles.map((style) => style._insertCss());
return () => removeCss.forEach((dispose) => dispose());
};
hydrate(
<StyleContext.Provider value={{ insertCss }}>
<BrowserRouter>
<App />
</BrowserRouter>
</StyleContext.Provider>,
document.getElementById('app')
);
and here is a component inside the App.js which uses the css styling that does not work.
import React from 'react';
import { NavLink } from 'react-router-dom';
import style from './css/style.css';
import withStyles from 'isomorphic-style-loader/withStyles';
function Navbar() {
const languages = [
{
name: 'All',
param: 'all',
},
{
name: 'JavaScript',
param: 'javascript',
},
{
name: 'Ruby',
param: 'ruby',
},
{
name: 'Python',
param: 'python',
},
{
name: 'Java',
param: 'java',
},
];
return (
<ul className='navbar'>
{languages.map(({ name, param }) => (
<li key={param}>
<NavLink
activeStyle={{ fontWeight: 'bold' }}
to={`/popular/${param}`}
>
{name}
</NavLink>
</li>
))}
</ul>
);
}
export default withStyles(style)(Navbar);
I faced the same problem. Problem is related with css-loader. By default, css-loader generates JS modules that use the ES modules syntax. isomorphic-style-loader needs a CommonJS modules syntax.
Try this:
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false,
},
}

Webpack React error: You should not use <Switch> outside a <Router>

so i'm trying to setup server side rendering with my React app and I have solved every problem so far but I have run into this one which I cannot solve after a few days of trying.
I'm using the latest of everything included and I run into this error: Error: Invariant failed: You should not use <Switch> outside a <Router>
Below is the react app index
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
const SSR = (
<BrowserRouter>
<App/>
</BrowserRouter>
);
if(typeof document === "undefined") {
module.exports = SSR;
} else {
ReactDOM.hydrate(SSR, document.getElementById("app"));
}
And the component
import React from 'react';
import { Route, Switch } from 'react-router-dom'
import Home from './components/index'
const NavRoute = ({exact, path, component: Component}) => (
<Route exact={exact} path={path} render={(props) => (
<React.Fragment>
<Navbar/>
<Component {...props}/>
</React.Fragment>
)}/>
)
export default class App extends React.Component {
render() {
return (
<Switch>
<NavRoute exact={true} path="/" component={Home}/>
</Switch>
);
}
}
And my webpack config
const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
module.exports = [
{
entry: {
client: './server/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: "[name].js",
publicPath: '/',
},
target: 'node',
externals: [nodeExternals()],
module: {
rules: [
{ test: /\.jsx?/, loader: "babel-loader"}
]
},
},
{
entry: {
bundle: './src/App.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: "[name].js",
publicPath: '/',
libraryTarget: "umd",
globalObject: "this",
},
module: {
rules: [
{ test: /\.jsx?/, loader: "babel-loader"}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.BROWSER': JSON.stringify(true),
}),
]
}
]
any help is appreciated let me know if I missed any info out, thank you.

Navigate to a specific page by url in React

How do I navigate to a specific page with url in React?
I realized I've used CRA in the past and haven't really tackled this specifically.
Currently I'm rendering ReactDOM with BrowserRouter and Switch with exact path in Routes
// index.jsx
/* eslint-disable import/extensions */
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './app/App.jsx';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
);
// App.jsx
import React from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import Header from '../components/Header';
import HomePage from '../pages/Home';
import PlansPage from '../pages/Plans';
import NotFoundPage from '../pages/NotFound';
const App = () => (
<BrowserRouter>
<Header />
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/plans" component={PlansPage} />
<Route component={NotFoundPage} />
</Switch>
</BrowserRouter>
);
export default App;
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
module.exports = {
devtool: false,
module: {
rules: [
{
test: /\.m?(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.scss$/,
use: [{
loader: 'style-loader',
options: {
sourceMap: true,
},
}, {
loader: 'css-loader',
options: {
sourceMap: true,
},
}, {
loader: 'sass-loader',
options: {
sourceMap: true,
},
}],
},
],
},
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js',
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: './index.html',
}),
new webpack.SourceMapDevToolPlugin({}),
],
};
Navigating to http://localhost:8080/plans leads to a page with Cannot GET /plans. However, clicking on a Link element navigates to that plans page without an issue.
Yeah since you're using webpack Dev Server all you have to do is add historyApiFallback=true and it'll fix you issue. You're essentially telling all routes to fall back to your index.html file. Hope that helped.

react-router v4 browserRouter is not working

i don't know what is diffrence in my code.
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AsyncChunkNames = require('webpack-async-chunk-names-plugin');
const lunaRocketModulesPath = path.resolve(__dirname, 'luna-rocket');
module.exports = {
entry: [
'#babel/polyfill',
path.join(__dirname,'src/app','app.js')
],
output: {
path: path.join(__dirname,'build'),
filename: 'index.bundle.js',
chunkFilename: '[name].bundle.js',
publicPath: '/', // 헐랭이.. 이 게 뭐길래...
},
mode: process.env.NODE_ENV || 'development',
resolve: {
alias: {
'luna-rocket': lunaRocketModulesPath
},
extensions: [
'.js',
],
},
devServer: {
contentBase: path.join(__dirname,'src'),
disableHostCheck: true,
historyApiFallback: true
},
module: {
rules: [
{
// this is so that we can compile any React,
// ES6 and above into normal ES5 syntax
test: /\.(js)$/,
// we do not want anything from node_modules to be compiled
exclude: /node_modules/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.(css|scss)$/,
use: [
"style-loader", // creates style nodes from JS strings
"css-loader", // translates CSS into CommonJS
"sass-loader" // compiles Sass to CSS, using Node Sass by default
]
},
{
test: /\.(jpg|jpeg|png|gif|mp3|svg)$/,
loaders: ['file-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname,'src','index.html'),
inject: 'body',
}),
// new AsyncChunkNames()
],
optimization: {
splitChunks:{
cacheGroups: {
default: false,
vendors: false,
vendor: {
name: 'vender',
chunks: "all",
test: "/node_module/",
priority: 20
},
common: {
name: 'common',
minChunks: 2,
chunks: "all",
priority: 10,
reuseExistingChunk: true,
enforce: true
}
}
}
}
};
app.js
import React, { Suspense, lazy } from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Route, Switch, HashRouter, withRouter, useRouterHistory , Redirect} from 'react-router-dom'
import Home from './Home';
import RocketComponent from './RocketComponent';
const Loading = () => <div>loading...</div>
ReactDOM.render(
<Router>
<div className="app">
<div className="container">
<Suspense fallback={<Loading />}>
<Route exact path="/" component={Home} />
<Route path="/rocket" component={RocketComponent} />
</Suspense>
</div>
</div>
</Router>,
document.getElementById('app')
);
RocketComponent.js
import HeaderNew from "./HeaderNew";
import React, {lazy, Suspense} from 'react';
import RocketMenuNew from "./RocketMenuNew";
import {Route, Switch} from "react-router-dom";
function scrollToTop() {
document.body.scrollTop = 0
}
const menuData = [
{
title: "LUXAccordion",
path: "/rocket/LUXAccordion",
component: "./Documents/LUXAccordion/AccordionDocument"
},
{
title: "LUXActionBar",
path: "/rocket/LUXActionBar",
component: "./Documents/LUXActionBar/ActionBarDocument"
},
{
title: "LUXBadge",
path: "/rocket/LUXBadge",
component: "./Documents/LUXBadge/BadgeDocument"
},
{
title: "LUXButton",
path: "/rocket/LUXButton",
component: "./Documents/LUXButton/ButtonDocument"
}
]
function DynamicLoader(props) {
// console.log("title", `./Documents/${title.title}/${title.title.substring(3)}Document`)
// const LazyComponent = React.lazy(() => import(`./Documents/${title.title}/${title.title.substring(3)}Document`));
const LazyComponent = lazy(() => import(`${props.component}`));
console.log("LazyComponent", LazyComponent)
return (
<LazyComponent />
);
}
class RocketComponent extends React.Component {
render() {
console.log("this.props.match.path", this.props.match.path)
return (
<div className="documents-new">
<HeaderNew />
<RocketMenuNew />
<Switch>
{menuData.map((props, i) => {
return <Route path={props.path} render={() => <DynamicLoader component={props.component}/>} key={i}/>
})}
</Switch>
</div>
);
}
}
export default RocketComponent
this code is working.
but RocketComponent.js moves to the route directory. is not working.
i don't know why??
RocketComponent.js --> path: app/router/RocketComponent.js
import HeaderNew from "./../HeaderNew";
import React, {lazy, Suspense} from 'react';
import RocketMenuNew from "./../RocketMenuNew";
import {Route, Switch} from "react-router-dom";
function scrollToTop() {
document.body.scrollTop = 0
}
const menuData = [
{
title: "LUXAccordion",
path: "/rocket/LUXAccordion",
component: "./../Documents/LUXAccordion/AccordionDocument"
},
{
title: "LUXActionBar",
path: "/rocket/LUXActionBar",
component: "./../Documents/LUXActionBar/ActionBarDocument"
},
{
title: "LUXBadge",
path: "/rocket/LUXBadge",
component: "./../Documents/LUXBadge/BadgeDocument"
},
{
title: "LUXButton",
path: "/rocket/LUXButton",
component: "./../Documents/LUXButton/ButtonDocument"
}
]
function DynamicLoader(props) {
const LazyComponent = lazy(() => import(`${props.component}`));
return (
<LazyComponent />
);
}
class RocketComponent extends React.Component {
render() {
return (
<div className="documents-new">
<HeaderNew />
<RocketMenuNew />
<Switch>
{menuData.map((props, i) => {
return <Route path={props.path} render={() => <DynamicLoader component={props.component}/>} key={i}/>
})}
</Switch>
</div>
);
}
}
export default RocketComponent
i modify the component path and app.js modify RocketComponent path right.
but is not working
the error is
why is not working, the path is right, please help me .
my webpack is 4, babel 7

React-router 4 with server side rendering: TypeError: Cannot read property 'pathname' of null

I'm building an React app using webpack#2 and React-Router#4. In development mode all works fine but for production I want to use server side rendering and it leads me to the error:
TypeError: Cannot read property 'pathname' of null
at isActive (/react-app/node_modules/react-router/Link.js:120:46)
at Object.children (/react-app/node_modules/react-router/Link.js:80:24)
at Subscriber.render (/react-app/node_modules/react-broadcast/Subscriber.js:65:23)
at /react-app/build/app.js:18:2455
at s (/react-app/build/app.js:17:22003)
at x._renderValidatedComponentWithoutOwnerOrContext (/react-app/build/app.js:18:2433)
at x._renderValidatedComponent (/react-app/build/app.js:18:2741)
at x.performInitialMount (/react-app/build/app.js:17:28173)
at x.mountComponent (/react-app/build/app.js:17:26586)
at Object.s.mountComponent (/react-app/build/app.js:1:27885)
Here is my App component:
import React from 'react';
import { render } from 'react-dom';
import { Match, Miss, Link } from 'react-router';
import Home from './Home';
import About from './About';
import NoMatch from './NoMatch';
export default () => (
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<hr/>
<Match exactly pattern="/" component={Home} />
<Match pattern="/about" component={About} />
<Miss component={NoMatch}/>
</div>
);
Here is an entrypoint for webpack:
import React from 'react';
import express from 'express';
import morgan from 'morgan';
import path from 'path';
import { ServerRouter, createServerRenderContext } from 'react-router';
import { renderToString } from 'react-dom/server';
import App from './components/App';
const app = express();
app.use(morgan('combined'));
app.use(express.static(path.join(__dirname)));
app.set('view engine', 'pug');
app.set('views', path.join(__dirname, '../src/views'));
app.get('*', function(req, res) {
const context = createServerRenderContext();
let markup = renderToString(
<ServerRouter
location={req.url}
context={context}
>
<App/>
</ServerRouter>
);
const result = context.getResult();
if (result.redirect) {
res.writeHead(301, {
Location: result.redirect.pathname
});
res.end();
} else {
if (result.missed) {
res.writeHead(404);
markup = renderToString(
<ServerRouter
location={req.url}
context={context}
>
<App/>
</ServerRouter>
);
const result = context.getResult();
if (result.redirect) {
res.writeHead(301, {
Location: result.redirect.pathname
});
res.end();
} else {
if (result.missed) {
res.writeHead(404);
markup = renderToString(
<ServerRouter
location={req.url}
context={context}
>
<App/>
</ServerRouter>
);
}
res.render('index', { includeCss: true, reactMarkup: renderToString(<App />) });
res.end();
}
});
app.listen(3000);
And finally webpack's config:
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const config = {
target: 'node',
entry: [
path.join(__dirname, 'src/js/server.prod.js')
],
output: {
path: path.join(__dirname, 'build'),
filename: 'app.js'
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(eot|png|svg|ttf|woff|woff2)$/,
use: 'file-loader'
}
]
},
externals: fs.readdirSync('node_modules').reduce(function(acc, mod) {
if (mod === '.bin') {
return acc;
}
acc[mod] = 'commonjs ' + mod;
return acc;
}, {}),
node: {
console: false,
global: false,
process: false,
Buffer: false,
__filename: false,
__dirname: false,
},
plugins: [
new webpack.optimize.UglifyJsPlugin()
],
bail: true
};
module.exports = config;
Any ideas? 😞
In Home.js and About.js
import { Link } from 'react-router';
The Link component you are importing is from 'react-router' this is the problem, this will work for react-router version < 4.0. From react-router version 4, this Link package is part of react-router-dom. So you need to install react-router-dom first:
npm install --save react-router-dom
and import like this:
import { Link } from 'react-router-dom'

Resources