React router can't find page after page refresh [duplicate] - javascript

This question already has answers here:
React-router with BrowserRouter / browserHistory doesn't work on refresh
(2 answers)
Closed 5 years ago.
My App is displaying a list of books with links to a detail page about the book.
When I am on the home component where the book list is rendered and click on a link to get to a specific book, the page is loading correctly...
But when I refresh the page, there is a 404 - Page not found error
(I'm using react-router v4, Node v6.11.1, Express v4.15.2)
React Router setup:
import { BrowserRouter as Router } from 'react-router-dom'
import { Switch, Route } from 'react-router-dom'
class App extends Component {
render() {
return (
<div>
<MyNavbar/>
<div style={{paddingTop: "5rem"}}>
<Switch>
<Route exact path='/' component={Home}/>
<Route path='/book/:id' component={BookPage}/>
</Switch>
</div>
</div>
)
}
}
ReactDOM.render((
<Router>
<App />
</Router>
), document.getElementById('root'))
This is my webpack config:
var webpack = require('webpack')
var path = require('path')
module.exports = {
entry: {
app: './src/app.js'
},
output: {
filename: 'public/dist/bundle.js',
sourceMapFilename: 'public/dist/bundle.map.js'
},
devtool: '#source-map',
module: {
loaders: [
{
loader: 'babel-loader',
test: /\.js?$/,
exclude: /(node_modules)/,
query: {
presets: ['react', 'es2015']
}
}, {
test: /\.css$/,
loader: "style-loader!css-loader"
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
{ loader: 'sass-loader', options: { sourceMap: true } }
]
},
{
test: /\.(png|jpg|)$/,
loader: 'url-loader?limit=200000&name=./imgs/[hash].[ext]'
}
]
},
}

this is the doc~
if you are use webpack-dev-server
if you write the webpack-dev-server config on webpack.config.js
You should add this historyApiFallback: true to devServer on your webpack config.
This will return index.html when 404.
devServer: {
...
historyApiFallback: true
}
if you only use cli,do this
webpack-dev-server --history-api-fallback
if you use the node express server.js,see here
If production, you can edit the server config, make every request,return index.html, sure, exclude static files and api requst~
Sorry. I write this on mobile phone.

Related

React Routing Issue (Without create-react-app)

I have created react app without using CRA command and also installed react router dom v6.
When I have 1 path in url, I don't get any error (like http://localhost:3000 /firstPath ). But, when I have 2 paths in URL (like http://localhost:3000 /firstPath/secondPath ), I get 404 firstPath/main.js not found error. It is searching for main.js inside a folder called firstPath.
In the following code, the index, add and test URLs are working. But the edit URL is not working.
Console Log Image
Sources Tab Image
rootRouter.js code: [BrowserRouter tag is given in the parent file]
import React from "react";
import { Route, Routes } from "react-router-dom";
import Home from "../views/Home";
import Add from "../views/Add";
import Edit from "../views/Edit";
const rootRouter = () => {
return (
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="add" element={<Add />} />
<Route path="edit/:id" element={<Edit />} />
<Route path="test" element={<div>test</div>} />
</Routes>
);
};
export default rootRouter;
webpack.config.js code:
const path = require("path");
const port = process.env.PORT || 3000;
module.exports = {
mode: "development",
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "main.js",
},
target: "web",
devServer: {
host: "localhost",
port: port,
static: ["./public"],
open: true,
hot: true,
liveReload: true,
historyApiFallback: true,
},
resolve: {
extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
{
test: /\.png?$/,
use: ["file-loader"],
},
],
},
};

React-router cannot get component when manually modifying url or page refresh [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.

React-hot-loader doesn't work with React-router-dom

So I've finally setup a working project with:
Electron (2.0.2)
React (16.4.0)
React-router-dom (4.2.2)
Webpack (4.11.0)
React-hot-loader (4.2.0)
And just when I started to develop some react components I noticed my project won't hot reload correctly. If I adjust something on the base url (/) it is updated correctly, but if I update something on a secondary url, say /test the webpack compiles, but I get the message Cannot GET /test.
I've tried a lot and I cannot seem to figure out what I am doing wrong. I looked into react-router-dom, since hot-reloading was an issue back in version 3.x, but they say it should be resolved now (in 4.x --> It works here..). Also i've added <base href="/"/> in my index.html so that is not it.
Can anyone tell me what I am doing wrong?
Webpack.common.js (This is merged into Webpack.dev.js)
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
},
resolve: {
modules: [path.resolve(__dirname), 'node_modules']
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
cacheDirectory: true,
presets: ['env', 'react'],
plugins: ['transform-runtime'],
env: {
development: {
plugins: ['react-hot-loader/babel']
},
production: {
presets: ['react-optimize']
}
}
}
}
]
}
};
Webpack.dev.js
module.exports = merge(common, {
mode: 'development',
devtool: 'eval-source-map',
entry: {
'app': [
'babel-polyfill',
'react-hot-loader/patch',
path.join(__dirname, 'src', 'index.js')
]
},
plugins: [
new webpack.HotModuleReplacementPlugin() // Enable hot module replacement
]
});
Index.js
import React from "react";
import ReactDOM from "react-dom";
import { AppContainer } from "react-hot-loader";
import { App } from "./app";
const render = Component => {
ReactDOM.render(
<AppContainer>
<Component/>
</AppContainer>,
document.getElementById("root")
);
};
render(App);
if (module.hot) {
module.hot.accept("./app", () => {
render(App);
});
}
App.js (my main entry point for my app, thus where I define my base routing)
import React, { Component } from 'react';
import { BrowserRouter, Route, NavLink } from 'react-router-dom';
import { Test } from './components/test';
import { Test2 } from './components/test2';
export class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<NavLink to="/">Home</NavLink>
<NavLink to="/test">Test</NavLink>
<div>
<Route exact path="/" component={Test}/>
<Route path="/test" component={Test2}/>
</div>
</div>
</BrowserRouter>
);
}
}
And the components 'test' and 'test2' are just plain simple react components with a 'hello world' text.
Anyone who sees anything that I am missing or doing wrong?
Thanks to this tutorial I found a way to adapt my project and get hot loading to work. It even made my code a bit cleaner and my build scripts simpeler.
Webpack.common.js
The first thing I needed to change was the babel-loader. I stole it from some tutorial, and it worked, but I did not know exactly what it did so I got rid of that code. I've also made the compilation of my code faster through the webpack.DllReferencePlugin.
Here is the updated webpack.common.js:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
module.exports = {
entry: {
app: [
'babel-polyfill',
'./src/index.js',
],
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
plugins: ['react-hot-loader/babel'],
cacheDirectory: true,
presets: ['env', 'react'],
},
}
],
},
plugins: [
new webpack.DllReferencePlugin({
context: path.join(__dirname),
manifest: require('../dist/vendor-manifest.json'),
}),
new HtmlWebpackPlugin({
title: '<my-app-name>',
filename: 'index.html',
template: './public/index.html',
}),
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, '../dist/*.dll.js'),
includeSourcemap: false // add this parameter
})
],
};
The AddAssetHtmlPlugin is required since the index.html is dynamically created (by the HtmlWebpackPlugin) for the dev server and you cannot hardcode the correct bundle import for the vendor.dll and the app.bundle (more here).
webpack.dev.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const webpack = require('webpack');
const path = require('path');
module.exports = merge(common, {
mode: 'development',
devtool: 'eval-source-map',
devServer: {
hot: true,
contentBase: path.resolve(__dirname, 'dist'),
historyApiFallback: true // Allow refreshing of the page
},
plugins: [
new webpack.HotModuleReplacementPlugin(), // Enable hot reloading
]
});
What did I change:
I moved the entry point up to webpack.common.
I Removed the 'react-hot-loader/patch' from the entry
(optional) I've added some config options for the webpack-dev-server.
Index.js
This is the file that caused the hot-reload to fail. Especially the if(module.hot) part caused it to fail. So I've changed it to the following:
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { App } from './app';
const render = () => {
ReactDOM.render(
<AppContainer>
<App/>
</AppContainer>,
document.getElementById('app'),
);
};
render(App);
if (module.hot) {
module.hot.accept('./app', () => {
const NextApp = require('./app').default; // Get the updated code
render(NextApp);
});
}
The reason it works now is because now I fetch the new app and replace the old one, thus telling the hot-loader there has been a change. I could also just use module.hot.accept(), but that would make the react-hot-loader useless (you make use of the webpack hot-reloader) and this way I would also lose the state within my components every time I updated some code.
So there you go. I hope this will help anyone (other then myself).

Route giving error on reload

So I have routing working with redux, but I have a problem with reloading in one of the routes.
app.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, IndexRoute } from 'react-router';
import { Provider } from 'react-redux';
import store , { history } from './store';
import Connect from './components/Connect';
import PhotoList from './components/PhotoList';
import PhotoContent from './components/PhotoContent';
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={Connect}>
<IndexRoute component={PhotoList}></IndexRoute>
<Route path="view/:id" component={PhotoContent}></Route>
</Route>
</Router>
</Provider>,
document.getElementById("app")
);
webpack.config.js:
var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var path = require('path');
module.exports = {
context: path.join(__dirname, "src"),
devtool: debug ? "inline-sourcemap" : null,
entry: "./js/app.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: "app.min.js"
},
plugins: debug ? [] : [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: false }),
],
};
This is my code, history = syncHistoryWithStore(store, browserHistory). Then I try to reload the page while inside one of the view/:id, and I get the error:
GET http://localhost:8080/view/app.min.js
Do you guys have any idea why this is happening?
Any help would be appreciated.Thanks!
I run the webpack server from command line with:
webpack-dev-server --content-base src --inline --hot --history-api-fallback
Make sure your index.html is referencing the correct bundle file.
For webpack, make sure your config uses this:
config.devServer = {
...
contentBase: 'src',
historyApiFallback: true,
...
};
or, if you declared it as a single object:
{
...
devServer: {
...
contentBase: 'src',
historyApiFallback: true
}
}
It will redirect your requests to / and have react handle it.
Run your webpack-dev-server like this: ./node_modules/.bin/webpack-dev-server --inline --hot
EDIT:
While working with you over chat, I noticed that the bundle reference in your index.html was incorrect. Moving it to ../app.min.js fixed it.

jQuery needed for Bootstrap in React project

I am developing a React.js project where instead of using React-Bootstrap, I am loading the CSS of Bootstrap into my project. I am now needing to import the jQuery so that I can use dropdown menus etc.
Entry point file (index.js) - in the hope it would work
'use strict'
import React from 'react'
import { Route, Router, IndexRoute, browserHistory } from 'react-router'
import { render } from 'react-dom'
// Pages
import Main from './pages/main/main'
import Home from './pages/home/home'
import About from './pages/about/about'
window.jQuery = window.$ = require('jquery/dist/jquery.min')
import '../node_modules/bootstrap/dist/css/bootstrap.min.css'
import '../node_modules/bootstrap/dist/js/bootstrap.min.js'
render((
<Router history={browserHistory}>
<Route name='home' path='/' component={Main}>
<IndexRoute component={Home} />
<Route name='about' path='about' component={About} />
</Route>
</Router>), document.getElementById('app'))
Webpack file
'use strict'
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: [
'./browser/index'
],
output: {
path: path.join(__dirname, 'public', 'js'),
filename: 'bundle.js',
publicPath: '/public/js/'
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
new webpack.NoErrorsPlugin()
],
module: {
loaders: [
{ test: /\.js$/,
loader: 'babel-loader',
query: {
presets: [ 'es2015', 'react' ]
},
include: path.join(__dirname, 'browser')
},
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file'},
{test: /\.(woff|woff2)$/, loader: 'url?prefix=font/&limit=5000'},
{test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream'},
{test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml'},
{ test: require.resolve('jquery'), loader: 'imports?jQuery=jquery' }
]
}
}
The error appears in Chrome Dev tools: 'Bootstrap's Javascript requires jQuery'. I'm wondering whether perhaps I am missing something in terms on loaders in the webpack file, or need an import statement in the entry file?
Please make sure you have installed 'imports-loader', (npm install --save imports-loader). And in module.exports try this:
{ test: /bootstrap.+\.(jsx|js)$/, loader: 'imports?jQuery=jquery,$=jquery,this=>window' }
The really nice post regarding it I've found here: http://reactkungfu.com/2015/10/integrating-jquery-chosen-with-webpack-using-imports-loader/
Unfortunately provided solution doesn't work for me. In the case above Bootstrap and jQuery are both installed as npm dependencies under node_modules directory? I'm using WebPack 2, so I have converted the loader element as follows:
{
test: /bootstrap.+\.(jsx|js)$/,
use: 'imports-loader?jQuery=jquery,$=jquery,this=>window',
exclude: /node_modules/
}

Categories

Resources