React Router not working when URL contains multiple paths - reactjs

I'm using the webpack-dev-server for development and equally react router. This is my wepack configuration.
const webpack = require('webpack');
const path = require('path');
const common = require('./common.config.js')
const merge = require('webpack-merge')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = merge(common, {
devtool: 'source-map',
mode:'development',
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
{
// Adds CSS to the DOM by injecting a `<style>` tag
loader: 'style-loader'
},
{
// Interprets `#import` and `url()` like `import/require()` and will resolve them
loader: 'css-loader'
},
{
// Loader for webpack to process CSS with PostCSS
loader: 'postcss-loader',
options: {
plugins: function () {
return [
require('autoprefixer')
];
}
}
},
{
// Loads a SASS/SCSS file and compiles it to CSS
loader: 'sass-loader'
}
]
}
]
},
devServer:{
contentBase: path.join(__dirname, '../dist'),
port: 3000,
proxy:{
'/api': 'http://127.0.0.1:5000',
},
historyApiFallback:true,
overlay: true,
stats: 'normal'
},
watchOptions: {
ignored: /node_modules/
},
plugins: [
new CleanWebpackPlugin(['../dist'], { exclude:['sw.js']}),
new HtmlWebpackPlugin({
title: 'Script App - Dev',
filename: '../dist/index.html',
template:'index_template.html'
}),
new MiniCssExtractPlugin({
filename: 'bundled_styles.css'
}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
});
here is the entry point to my app (where I've defined the routes)
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import {BrowserRouter, Route, Switch} from 'react-router-dom'
import configureStore from './store/configureStore';
import FirstComponent from './FirstComponent';
import SecondComponent from './SecondComponent';
import App from './App';
const store = configureStore();
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App>
<Switch>
<Route path="/first/:a_code" component={FirstComponent} />
<Route path="/second" component={SecondComponent} />
</Switch>
</App>
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
I'm using react-router v4 and wepack v4.29.
My problem: When I push routes from my code using the history prop, everything works well but when I refresh my browser with the route, Everything goes blank. This behavior is observed only with the route that has multiple paths(/first/:param).
What I've tried: I read from some SO posts that I had to set the historyApiFallback property in my webpack config to true, and I did but it still did not help.
One of the comments in this github issue says that historyApiFallback will not work well if certain proxy configurations are available in the webpack config.
I don't want to remove these proxy configurations because I'm making api calls to another server running on a different port.
Can anyone help me on this? Please!

I actually missed a webpack configuration. I had to add an output configuration and set the publicPath to /. i.e
output:{
publicPath: '/'
}
I also added the publicPath in the devServer config. i.e
devServer:{
...
publicPath:'/',
historyApiFallback: true
}
Update:
Equally make sure that your bundle.js script is inserted in your html file like this:
<script src="/bundle.js"></script>
AND NOT
<script src="./bundle.js"></script> OR <script src="bundle.js"></script>

I had a similar situation, and I solved using the render: func, that avoids the component to be remounted when the location matches.
So my route look´s like this
<Route
path="/first/:a_code"
render={props =>
<FirstComponent {...props}/>
}
/>
and then in my component I get the params with match
const first_component = (props) => {
return (
<div>
{props.match.params.a_code}
</div>
)
}
at the url "/first/something" I can refresh the browser and still get the desired params, and without any error or blank page. And I don´t believe is a config issue because I didn´t eject the webpack configs in this project so far.

Related

React router dom is not working in the main app

Good morning,
I am having an issue when working with React router dom using my own webpack configuracion. I know using create-react-app will solve (almost) any issues and is friendly to use, but we would like to have more flexibility.
This is my main application:
import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import Login from '../pages/Login'
import { AppContextProvider } from '../context/AppContext'
const MainApp = () => {
return (
<AppContextProvider>
<Router basename={'nynweb'}>
<Switch>
<Route exact path='/' component={Login} />
<Route exact path='/login' component={Login} />
</Switch>
</Router>
</AppContextProvider>
)
}
export default MainApp
As you can see, it´s very simple. If I use localhost:3000/nynweb in the browser, it works perfectly and the router redirects to the login page. Then, If I type localhost:3000/nynweb/login, it should do exactly the same (at list to my understanding), but it doesn´t. I have the Cannot GET /nynweb/login response instead.
Internally the routing is working, though. I mean, when in the components I use the useHistory push method, it redirects properly (history.push('/login')).
What am I missing? Below I am posting my webpack configuration. Maybe the problem lies there:
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const BabelRules = {
test: /\.(js)$/,
use: ['babel-loader'],
exclude: /node_modules/,
}
const CSSRules = {
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
}
const ImageRules = {
test: /\.(woff(2)?|ttf|otf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'static/fonts/',
},
},
],
}
module.exports = {
entry: './src/index.js',
output: {
path: path.join(__dirname, '/dist'),
filename: 'bundle.js',
publicPath: '/nynweb/',
clean: true,
},
optimization: {
minimize: true,
},
devServer: {
port: 3000,
historyApiFallback: true,
},
module: {
rules: [BabelRules, CSSRules, ImageRules],
},
resolve: {
extensions: ['.js'],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
],
}
Below I have included a screenshot with the applicacion tree, so that you can see where the static folder is located (all the assets lie there)
As the documentation of Webpack states, the publicPath option allows to serve assets, like images, SVGs etc. What's probably going on is Webpack searching for an asset with the name 'login', but can't find it.
I'd advise you to change this string to your public folder where you store assets (typically, /public).
Also, the example in React-Router documentation adds a forward slash before the basename string.
Clarifications
You set your publicPath variable to /nynweb/, and what Webpack understands is that everything under the '/nynweb/*' path is to be served as an asset.
Change it to, let's say, /nynweb/public/, and now, everything under the '/nynweb/public/*' path is to be served as an asset.
Routes that are not '/nynweb/public/some_route' will now work as expected.

React router 4 url issue

import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import { history } from './history';
import Login from './components/Login';
import Home from './components/Home';
import NotFound from './presentation/NotFound';
import MainContent from './components/MainContent';
import Overview from './components/Overview';
export default () => {
return(
<Router history ={history}>
<Switch>
<Route path="/" component={Login} exact />
<Home>
<Route path="/main" exact component={MainContent} />
<Route path="/admin/:id" exact component={Overview} />
</Home>
<Route component={ NotFound } />
</Switch>
</Router>
);
}
my webpack config is
const path = require('path');
var webpack = require('webpack');
module.exports = {
entry: './src/main.js',
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js'
},
module: {
rules: [{
loader: 'babel-loader',test: /\.js$/,exclude: /node_modules/},
{
test: /\.s?css$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}, {
test: /\.(png|jpg|gif)$/,
use: [
'file-loader',
],
},{
test: /\.svg$/,
use: ['#svgr/webpack'],
}, {
test: /\.ttf$/,
loader: "file-loader?limit=10000"
}
]
},
devtool: 'cheap-module-eval-source-map',
devServer: {
contentBase: path.join(__dirname, 'public'),
historyApiFallback: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
__CLIENT__: JSON.stringify(true),
__DEVELOPMENT__: true,
__DEVTOOLS__: true
}),
],
optimization:{}
};
So when i land on /main i.e. MainContent component it renders a ViewComponent which renders a list of admins and on click of an individual element of the list it gives the detailed view of the that element on the /admin/:id route which works fine.
but if i refresh or hit the url directly from the address bar, the
fonts' and the images' network request that were being rendered initially
from localhost:8080/somerandomid.ttf are now retrieved from
localhost:8080/admin/somerandomid.ttf throws a 400 bad request error and the styles change.
can anybody suggest how to rectify this error. i am calling a function and passing the id on click of an element from the list.
this.props.history.push('/admin/' + id);
and that ViewComponent is which renders the admin list is connected as export default withRouter(ViewComponent); to get this.props.history.push.
same goes with the image url too. localhost:8080/otherandomid.png is replaced and tried to get > from localhost:8080/admin/otherandomid.png and throws a 400 bad request error with no image
displaying.
You need to set up a base URL.
It can be done with HtmlWebpackPlugin:
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
__CLIENT__: JSON.stringify(true),
__DEVELOPMENT__: true,
__DEVTOOLS__: true
}),
new webpack.HtmlWebpackPlugin({
base: '/'
}),
],
Or directly in the <head> of your index.html file:
<base href="/">

React-router-v4 default route not working with Express backend

I'm using react-router-v4, React 16, and Express 4 for a web application. I use a BrowserRouter component for my main application to render all my routes. All of the component routes and the default route (404) render fine when requesting a single level subdirectory (example.com/asdf), but when one or more subdirectories are being requested (example.com/asdf/zzz), the default route fails to match the request and render, and I'm simply left with a blank page. Here is an example of my app.js with my routes:
import React, { Component } from 'react';
import {
BrowserRouter, Switch, Route
} from 'react-router-dom'
class App extends Component {
constructor() {
super();
}
render() {
return (
<BrowserRouter>
<Switch>
<ScrollToTopRoute exact path="/" component={Cover}/>
<ScrollToTopRoute path="/faq" component={FAQ}/>
<ScrollToTopRoute path="/signup" component={Signup}/>
<ScrollToTopRoute path="/login" component={Login}/>
<Route component={FourOhFour} />
</Switch>
</BrowserRouter>
);
}
}
export default App;
Like I said, I'm using Express for hosting this web application. These are the relevant routes for that.
// Serve up index as React app under build dir
app.use('/', express.static(path.join(__dirname, 'build')));
// Handles all other react routes so no 404 errors
app.get('/*', function (request, response){
response.sendFile(path.join(__dirname, 'build/index.html'));
});
If I console.log inside of the app.get route, on nested subdirectories like example.com/asdf/zzz, it seems to loop 4-5 times before finally ending on a blank screen. I've tried removing all other routes besides the default route in app.js, and the results are the same, it works fine until nested subdirectories.
EDIT: Someone asked for my webpack config, so here it is:
// webpack.config.js
const HtmlPlugin = require('html-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
module.exports = {
// Tell webpack to start bundling our app at src/index.js
entry: [
'./src/index.js'
],
// Output our app to the dist/ directory
output: {
filename: 'app.js',
path: path.resolve(__dirname, "build")
},
// Emit source maps so we can debug our code in the browser
devtool: 'source-map',
// Tell webpack to run our source code through Babel
module: {
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.css$/,
loader: [ 'style-loader', 'css-loader' ]
},
{
test: /\.(png|jpg|gif|svg|ico)$/,
loader: 'file-loader?name=./public/img/[hash].[ext]'
},
{
test: /\.(json)$/,
loader: 'file-loader?name=./public/assets/[hash].[ext]'
},
{
test: /\.(eot|ttf|woff|woff2)$/,
loader: 'file-loader?name=./public/fonts/[hash].[ext]'
},
{
test: /\.(html)$/,
options: {
attrs: ['link:href']
},
loader: 'html-loader'
}]
},
// Since Webpack only understands JavaScript, we need to
// add a plugin to tell it how to handle html files.
plugins: [
// Configure HtmlPlugin to use our own index.html file
// as a template.
// Check out https://github.com/jantimon/html-webpack-plugin
// for the full list of options.
new HtmlPlugin({
template: './public/index.html'
}),
new webpack.EnvironmentPlugin(['NODE_ENV'])
]
}
EDIT: Some more information. It seems like the Express routes are working, and rendering all non-existant routes using response.sendFile, but the default route appears to stop working when using nested subdirectories.

Cannot GET "/About" with react-router v4 (Production Help)

I've been reading over all the docs for react-router-dom (v4), and tons of Stack Overflow questions with my same error, but A) They leave a lot of unanswered holes and B) They seem to all be suggesting a development only fix, so I'm hoping to see what people are actually doing in PRODUCTION for this simple scenario and C) I'm probably doing something stupid and the answers aren't working for me, with the error "Cannot GET /about" rendering with no errors in the console.
I'm using Express, React, Node and using Webpack for compiling. I can successfully reach my homepage, and clicking any links takes me to the appropriate components, but manually typing in the URL breaks this, as discussed here and the reasons for this error discussed here.
Most answers suggest adding devServer.historyApiFallback = true and output.publicPath = '/' in the webpack.config.js file, which implies I also need to run npm install --save-dev webpack-dev-server and run it using node_modules/.bin/webpack-dev-server as suggested in the docs. Doing all of this, nothing happens. In fact, it's worse now because I also can't access my home route of '/'.
So before dropping my current config here, 1) What can I do to fix this? 2) Does it even matter? The webpack-dev-server is obviously for development only so what about production?
My webpack.config.js file:
var webpack = require('webpack');
var path = require('path');
var envFile = require('node-env-file');
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
try {
envFile(path.join(__dirname, 'config/' + process.env.NODE_ENV + '.env'))
} catch (e) {
}
module.exports = {
devServer: {
historyApiFallback: true,
},
entry: [
'script-loader!foundation-sites/dist/js/foundation.min.js',
'./app/app.jsx'
],
plugins: [
new webpack.DefinePlugin({
'process.env': {
//you don't get to see this
}
})
],
output: {
path: __dirname,
filename: './public/bundle.js',
publicPath: '/'
},
resolve: {
modules: [
__dirname,
'node_modules',
'./app/components',
'./app/api'
],
alias: {
app: 'app',
applicationStyles: 'app/styles/app.scss',
actions: 'app/actions/actions.jsx',
configureStore: 'app/store/configureStore.jsx',
reducers: 'app/reducers/reducers.jsx'
),
},
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0']
},
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/
},
{
loader: 'url-loader?limit=100000',
test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/
},
{
loader: 'sass-loader',
test: /\.scss$/,
options: {
includePaths: [
path.resolve(__dirname, './node_modules/foundation-sites/scss')
]
}
}
]
},
devtool: process.env.NODE_ENV === 'production' ? false : 'source-map'
};
My app.jsx:
var React = require('react');
var ReactDOM = require('react-dom');
import {Provider} from 'react-redux';
import {BrowserRouter as Router, Route, Switch, Link, HashRouter} from 'react-router-dom';
import Home from 'Home';
import Watch from 'Watch';
import About from 'About';
import AddShow from 'AddShow';
var store = require('configureStore').configure();
import firebase from 'app/firebase/';
// Load Foundation
$(document).foundation();
// App css
require('style-loader!css-loader!sass-loader!applicationStyles');
ReactDOM.render(
<Provider store={store}>
<Router>
<div>
<Route exact path="/" component={Home}/>
<Route path="/watch" component={Watch}/>
<Route path="/about" component={About}/>
<Route path="/addshow" component={AddShow}/>
</div>
</Router>
</Provider>,
document.getElementById('app')
);
You have to set up your web server (the one that serves index.html with the react app) to redirect all requests to the url of your index.html so that react-router can do its job. That's what the suggested change to webpack.config.js is doing for webpack-dev-server
In your webpack.config.js you need to enable the html plugin so webpack knows where your index.html is:
plugins: [
new webpack.DefinePlugin({
'process.env': {
//you don't get to see this
}
}),
new HtmlWebpackPlugin({
template: 'public/index.html' //or wherever your index.html is
})
],

Uncaught Error: Element type is invalid

I used React few month ago, now when I tried to use that code, made some changes in webpack config I'm getting errors.
I changed react-router to react-router-dom.
Whole routes file is here
https://github.com/mstanielewicz/react-simple-boilerplate/blob/master/app/config/routes.js
I'm getting:
Uncaught Error: Element type is invalid: expected a string (for
built-in components) or a class/function (for composite components)
but got: undefined. You likely forgot to export your component from
the file it's defined in.
I went through the code a few time and don't see any errors. Components and Containers are defined and exported correctly.
import React from 'react'
import {
BrowserRouter as Router,
Route,
hashHistory,
IndexRoute,
} from 'react-router-dom'
import { MainContainer, HomeContainer } from 'containers'
export default function getRoutes() {
return (
<Router history={hashHistory}>
<Router path='/' component={MainContainer} >
<IndexRoute component={HomeContainer} />
</Router>
</Router>
)
}
Directory structure looks like this
- app
|_components
| |_Home
| |_index.js
|_containers
|_Main
|_Home
|_index.js
with exports like this
export MainContainer from './Main/MainContainer'
export HomeContainer from './Home/HomeContainer'
And webpack cfg
var path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
template: path.join(__dirname, 'app/index.html'),
filename: 'index.html',
inject: 'body',
})
module.exports = {
devtool: 'cheap-module-inline-source-map',
entry: [
'./app/index.js',
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'index_bundle.js',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
],
},
],
},
resolve: {
modules: [
path.join(__dirname, 'app'),
'node_modules',
],
},
plugins: [HtmlWebpackPluginConfig],
}
Anyone can see a problem here?
Seems like a problem of import to me.
In react-router-dom, Router as been replaced by BrowserRouter. Or may be a bad default export, for example
import BrowserRouter from 'react-router-dom
instead of
import { BrowserRouter } from 'react-router-dom'
EDIT :
Your exports seems a bit weird.
Try to change import { MainContainer, HomeContainer } from 'containers' into
import MainContainer from './containers/Main/MainContainer'
import HomeContainer from 'containers/Main/HomeContainer'
and export default class FooContainer in the respective files.
There is also the fact you're using a Router inside a Router. Shouldn't it be a Route ?

Resources