react router - 404 in production - javascript

I have an app that uses react-router-dom and I have a a basic setup like so:
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { Box, Flex } from '#chakra-ui/react';
import CodeEntry from './pages/CodeEntry';
import Register from './pages/Register';
// import Login from './pages/Login';
import { useUI } from '../state/context/UIContext';
import { GlobalStyle } from '../styles/GlobalStyles';
import Header from './Header';
import Footer from './Footer';
import ErrorMessage from '../components/ErrorMessage';
import routes from '../config/routes';
const App = () => {
const {
state: { errorMessage },
} = useUI();
return (
<Router>
<GlobalStyle />
<Flex flexDir="column" justifyContent="space-between" minH="100vh">
<Box>
<Header />
{errorMessage.length > 0 && <ErrorMessage message={errorMessage} />}
<Switch>
<Route exact path={routes.APP.REGISTER}>
<Register />
</Route>
<Route exact path={routes.APP.DASHBOARD}>
<>Dashboard</>
</Route>
<Route exact path={routes.APP.HOME}>
<CodeEntry />
</Route>
<Route exact path={routes.APP.LOGIN}>
<>Login</>
</Route>
</Switch>
</Box>
<Box>
<Footer />
</Box>
</Flex>
</Router>
);
};
export default App;
When I run my app locally with npm run dev it uses the following command to spin up a local server:
webpack serve --config webpack.dev.js
the webpack.dev.js file looks like so:
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');
const Dotenv = require('dotenv-webpack');
const plugins = [];
//if (process.env.CI !== 'true') plugins.push(new Dotenv({ systemvars: true, path: path.resolve(__dirname, '.env') }));
module.exports = merge(common, {
mode: 'development',
devServer: {
host: 'localhost',
port: 3000,
historyApiFallback: true,
open: true,
},
infrastructureLogging: {
level: 'error',
},
stats: 'none',
output: {
publicPath: '/',
},
plugins,
});
and the webpack.common.js like so:
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const path = require('path');
require('babel-polyfill');
module.exports = {
entry: {
main: ['babel-polyfill', './src/index.tsx'],
},
devtool: 'source-map',
output: {
filename: '[name].[fullhash].js',
path: path.resolve('./dist'),
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
loader: 'ts-loader',
},
{
test: /\.js$/,
exclude: [path.resolve(__dirname, 'node_modules')],
use: [{ loader: 'babel-loader' }],
},
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
outputPath: 'assets/img/',
},
},
],
},
{
test: /\.(woff(2)?|ttf|eot|otf)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/fonts/',
},
},
],
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebPackPlugin({
template: 'index.html',
}),
new webpack.ProvidePlugin({
process: 'process/browser',
}),
new CleanWebpackPlugin({
cleanAfterEveryBuildPatterns: ['dist'],
}),
new webpack.DefinePlugin({
__REACT_DEVTOOLS_GLOBAL_HOOK__: '({ isDisabled: true })',
}),
],
};
Everything in dev works as it should but when I run a build for production non of my routes apart from the root route ('/') work. Here is how I run a production build:
npm run build && webpack --config webpack.prod.js
and here is how I serve that build locally using the npm package serve:
serve dist --no-clipboard --listen ${PORT:-3000}"
My webpack.prod.js file is quite simple and adopts a lot of the webpack config from the webpack.common.js config file. Here is my prod config:
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
//
module.exports = merge(common, {
mode: 'production',
});
I have tried adding a <Link> component like so:
<Link to='/register'>
The link works fine when I click on it in prod as well as dev, it is just when I navigate directly to the page where I get a 404

Solution was simple, in the end it was an issue with the serve package. All I had to do was a a -s flag to the following command:
serve -s dist --no-clipboard --listen ${PORT:-3000}"

Related

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>
);

React-Router subroutes not displaying

It seems that when webpack builds the file the output can only see the maincard div and none of the contents therein. I'm not sure what's missing as when this is run as npm react-scripts start it works fine. I'm not sure what i'm missing from webpack for this to render correctly. I'm trying to load this into an S3 bucket so it has to be packed with the webpack.
import React from 'react';
import { connect } from 'react-redux';
import { Route, withRouter } from 'react-router-dom';
import { fetchUserList } from "../actions/UserActions";
import { fetchSkillList } from "../actions/SkillActions";
import WelcomeCard from "./WelcomeCard";
import UserSearchCard from "./UserSearchCard";
import AddUserCard from './AddUserCard';
import '../styles/MainCard.css';
class MainCard extends React.Component {
componentDidMount() {
this.props.fetchUserList();
this.props.fetchSkillList();
}
render() {
return (
<div className="main_card">
<Route exact path='/' component={WelcomeCard}/>
<Route path='/list' component={UserSearchCard}/>
<Route path='/new' component={AddUserCard}/>
</div>
);
}
}
const mapDispatchToProps = dispatch => {
return {
fetchUserList: () => dispatch(fetchUserList()),
fetchSkillList: () => dispatch(fetchSkillList())
}
};
export default withRouter( connect(undefined, mapDispatchToProps)(MainCard) );
Webpack Config:
let path = require('path');
let webpack = require('webpack');
const publicPath = '/dist/build/';
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
//Content
entry: './src/index.js',
mode: 'development',
// A SourceMap without column-mappings ignoring loaded Source Maps.
devtool: 'cheap-module-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('development')
}
}),
//simplifies creation of HTML files to serve your webpack bundles. This is especially useful for webpack bundles that include a hash in the filename which changes every compilation. You can either let the plugin generate an HTML file for you, supply your own template using lodash templates or use your own loader.
new HtmlWebpackPlugin({
title: 'Talent Identification Manager'
}),
//Auto replacement of page when i save some file, even css
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
output: {
path: path.join(__dirname, publicPath),
filename: 'main.bundle-0.0.1.js',
publicPath: "/",
sourceMapFilename: 'main.map',
},
devServer: {
port: 3000,
host: 'localhost',
//Be possible go back pressing the "back" button at chrome
historyApiFallback: true,
noInfo: false,
stats: 'minimal',
publicPath: publicPath,
contentBase: path.join(__dirname, publicPath),
//hotmodulereplacementeplugin
hot: true
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules(?!\/webpack-dev-server)/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-2'],
plugins: ['syntax-decorators']
}
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
}
}
React Router doesn't know that you want to treat /talentidbucket as the base of your site, so you have explicitly tell it so by passing the base path as the basename prop of the BrowserRouter component in production.
class App extends React.Component {
render() {
return (
<BrowserRouter basename="/talentidbucket"> {/* ... */} </BrowserRouter>
);
}
}

webpack not working when introducing routes on react

I am using webpack 4 to automate some tasks for my web app.
Page shows blank on production mode, when running command: npm run build
it works with npm run start all good. The thing is webpack is not being able somehow to deal with this part:
<BrowserRouter><AppRoutes/></BrowserRouter>
And my index.js looks as:
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from 'react-router-dom';
import AppRoutes from './routes';
require('../src/assets/styles/main.scss');
console.log("I am being called index.js...");
ReactDOM.render(<BrowserRouter><AppRoutes/></BrowserRouter>, document.getElementById("index"));
while webpack looks like:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = (env, argv) => {
console.log("ENV DETECTED: " + argv.mode);
return {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: {
minimize: true
}
}
]
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
// 'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
minimize: true
}
},
{
loader: 'postcss-loader',
options: {
config: {
path: './postcss.config.js'
}
}
}
]
},
{
test: /\.scss$/,
use: [
argv.mode !== 'production' ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
minimize: true
}
},
{
loader: 'postcss-loader',
options: {
config: {
path: './postcss.config.js'
}
}
},
"sass-loader"
]
}
],
},
plugins: [
new CleanWebpackPlugin('dist', {}),
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
new MiniCssExtractPlugin({
filename: "main.css",
chunkFilename: "[id].css"
}),
require('autoprefixer'),
]
}
};
I dont know why on production the app is not showing the components at all!
ANy help on this?
Edited:
My routes.js looks as following:
import React from "react";
import { Switch, Route } from 'react-router-dom';
import Helloworld from './components/helloworld/helloworld.component';
import SecondView from './components/secondview/secondview.component';
import ThirdView from "./components/thirdview/thirdview.component";
const AppRoutes = () => (
<main>
<Switch>
<Route exact path='/' component={Helloworld}/>
<Route path='/secondview' component={SecondView}/>
<Route path='/thirdview' component={ThirdView}/>
<Route path='/thirdview/:number' component={ThirdView}/>
</Switch>
</main>
);
export default AppRoutes;
if I change <Route exact path='/' component={Helloworld}/> to <Route path='/' component={Helloworld}/> it works, but then I have problem navigation to other components, somehow my page url is different when I open the index.html on production mode.
The error I get when trying to navigate from helloworld component(after removing exact from Route tag) to secondview component is :
DOMException: Failed to execute 'pushState' on 'History': A history
state object with URL 'file:///secondview' cannot be created in a
document with origin 'null' and URL
'file:///home/webapp/Desktop/myapp/dist/index.html'.
Serve App With Express
Basically, when you change paths you are moving away from your index.html unless you serve the react app with a server of some kind. Try to setup an express server for index with something like this:
const express = require('express')
const path = require('path')
const port = process.env.PORT || 3000
const app = express()
// serve static assets eg. js and css files
app.use(express.static(__dirname + '/public'))
// Handles all routes
app.get('*', function (request, response){
response.sendFile(path.resolve(__dirname, 'public', 'index.html'))
})
app.listen(port)
console.log("server started on port " + port)
You can put this in your root directory as server.js, make sure index is being referred to in the sendFile line (right now assumes index.html is in /public directory), run this with babel-node server.js, and should serve your app on localhost:3000.

React Router wildcard not being matched [duplicate]

I have the following webpack config file:
var webpack = require('webpack');
var path = require('path');
var BUILD_DIR = path.resolve(__dirname, 'src/client/public');
var APP_DIR = path.resolve(__dirname, 'src/client/app');
var config = {
entry: [
APP_DIR + '/config/routes.jsx',
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8080'
],
output: {
publicPath: 'http://localhost:8080/src/client/public/'
},
module : {
loaders : [
{
test: /\.jsx?$/,
loader: 'babel-loader',
include: APP_DIR,
exclude: /node_modules/,
query: {
presets: ['es2015']
}
},
{
test: /\.scss$/,
loaders: [ 'style', 'css', 'sass' ]
},
{
test: /\.json$/,
loader: "json-loader"
}
]
}
};
module.exports = config;
all I am trying to do is run my app on localhost, however when I hit: "http://localhost:8080/src/client/home" (as per my routes.jsx and after running webpack-dev-server)
import React from 'react';
import { Route, Router, browserHistory } from 'react-router';
import ReactDOM from 'react-dom';
import Wrapper from './../components/wrapper.jsx';
import Home from './../components/home.jsx';
import Projects from './../components/projects.jsx';
import SingleProject from './../components/projectContent/singleProject.jsx';
import About from './../components/aboutUs.jsx'
ReactDOM.render((
<Router history={browserHistory} >
<Route path="/" component={Wrapper} >
<Route path="home" component={Home} />
<Route path="projects" component={Projects} />
<Route path="projects/:id" component={SingleProject} />
<Route path="about" component={About} />
</Route>
</Router>
), document.getElementById('app'));
I get
"Cannot GET /src/client/home".
First thing you have mentioned in your routes as the home component to have path /home. So you need to visit http://localhost:8080/home. Also if you try to access this url directly, it will give you this error since you are using browserHistory. If you want you can use hashHistory or HashRouter in react-router v4, in which case you will need to visit http://localhost:8080/#/home. If you want to continue using browserHistory or BrowserRouter as in react-router v4, then you will need to add historyApiFallback: true in you webpack
var webpack = require('webpack');
var path = require('path');
var BUILD_DIR = path.resolve(__dirname, 'src/client/public');
var APP_DIR = path.resolve(__dirname, 'src/client/app');
var config = {
entry: [
APP_DIR + '/config/routes.jsx',
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8080'
],
output: {
publicPath: 'http://localhost:8080/src/client/public/'
},
devServer: {
historyApiFallback: true
},
module : {
loaders : [
{
test: /\.jsx?$/,
loader: 'babel-loader',
include: APP_DIR,
exclude: /node_modules/,
query: {
presets: ['es2015']
}
},
{
test: /\.scss$/,
loaders: [ 'style', 'css', 'sass' ]
},
{
test: /\.json$/,
loader: "json-loader"
}
]
}
};
module.exports = config;
You need to add this in your webpack settings:
devServer: {
historyApiFallback: true,
},
And start your server like this:
webpack-dev-server --config webpack.config.js
Because you want React-Route to handle the route instead of your server. So no matter what the url is it should goes to index.html.

Issue with Webpack using React

Sorry if this is a duplicate question. I can't seem to solve this or find an answer. I have a basic Webpack setup i'm working with in conjunction with React and Django. Basically I'm getting compilation error's when using webpack.
These two webpack files aren't loading properly, both below, and neither is this App module (I think it's the provider component from redux).
I think it may be something to do with a driver - or something very simple that I've missed. I would really appreciate the help as I've been trying to fix this for a long time now haha!
Thanks!!!
webpack.config.local.config.js
var path = require("path")
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
var ip = 'localhost'
var config = require('./webpack.base.config.js')
config.devtool = "#eval-source-map"
config.entry = {
App1: [
'webpack-dev-server/client?http://' + ip + ':3000',
'webpack/hot/only-dev-server',
'./reactjs/App1',
],
}
config.output.publicPath = 'http://' + ip + ':3000' + '/assets/bundles/'
config.plugins = config.plugins.concat([
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new BundleTracker({filename: './webpack-stats-local.json'}),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('development'),
'BASE_API_URL': JSON.stringify('https://'+ ip +':3000/api/v1/'),
}}),
])
config.module.loaders.push(
{ test: /\.js?$/, exclude: /node_modules/, loaders: ['react-hot', 'babel'] }
)
module.exports = config
webpack.base.config.js
var path = require("path")
var webpack = require('webpack')
module.exports = {
context: __dirname,
entry: {
// Add as many entry points as you have container-react-components here
App1: './reactjs/App1',
vendors: ['react']
},
output: {
path: path.resolve('./djreact/static/bundles/local/'),
filename: "[name]-[hash].js"
},
externals: [
], // add all vendor libs
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js'),
], // add all common plugins here
module: {
loaders: [
// js
{
test: /\.js$/,
exclude: /node_modules/,
loaders: ['babel'],
presets: ['es2015', 'react'],
},
// PNG
{
test : /\.(png|jpg|svg)$/,
include : path.join(__dirname, 'img'),
loader : 'url-loader?limit=30000&name=images/[name].[ext]'
},
{
test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
loader: 'file-loader'
},
// CSS
{
test: /\.scss$/,
include: path.join(__dirname, 'client'),
loader: 'style-loader!css-loader!sass-loader'
}
] // add all common loaders here
},
resolve: {
modulesDirectories: ['node_modules', 'bower_components'],
extensions: ['', '.js', '.jsx']
},
}
The App that is having some errors
App1.js
import React from "react"
import { render } from "react-dom"
import {
createStore,
compose,
applyMiddleware,
combineReducers,
} from "redux"
import { Router, Route, hashHistory, IndexRoute } from 'react-router'
import { Provider } from "react-redux"
import thunk from "redux-thunk"
import * as reducers from "./reducers"
import App1Container from "./containers/App1Container"
let finalCreateStore = compose(
applyMiddleware(thunk),
window.devToolsExtension ? window.devToolsExtension() : f => f
)(createStore)
let reducer = combineReducers(reducers)
let store = finalCreateStore(reducer)
class App1 extends React.Component {
render() {
return (
<Provider store={store}>
<Router history = {hashHistory}>
<Route path="/" component={Layout}>
<IndexRoute component = {App1Container}/>
</Route>
</Router>
<App1Container />
</Provider>
)
}
}
render(<App1/>, document.getElementById('App1'))
So here are the errors.
From webpack.local.config.js
From webpack.base.config.js
webpack.base.config.js => jsx syntax error, I think that is wrong babel settings
Can you share your .babelrc?
Usually, this file is as follows.
{
"presets": ["es2015","react"]
}

Categories