Reputation: 1518
Usually, we will show the NotFound page to the user if the provided path is not matching using the below code
<Route path={"*"} component={NotFound} />
But when I add this, it always navigates me to the home page which is /
.
Not rendering the NotFound component.
Please help me to solve this one. The code is in this repository https://github.com/dhanushkumarsivaji/microfront-end
Webpack Code Files: -
container: (HOST)
app.js
import React, { lazy, Suspense } from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import {
StylesProvider,
createGenerateClassName,
} from '@material-ui/core/styles';
import { createBrowserHistory } from 'history';
import Progress from './components/Progress';
import Header from './components/Header';
const MarketingLazy = lazy(() => import('./components/MarketingApp'));
const AuthAppLazy = lazy(() => import('./components/AuthApp'));
const generateClassName = createGenerateClassName({
productionPrefix: 'co',
});
const history = createBrowserHistory();
function NotFound() {
return (
<h1>
Not Found Page
</h1>
)
}
export default () => {
return (
<Router history={history}>
<StylesProvider generateClassName={generateClassName}>
<div>
<Header/>
<Suspense fallback={<Progress />}>
<Switch>
<Route path='/auth' component={AuthAppLazy} />
<Route path="/" component={MarketingLazy} />
<Route path={"*"} component={NotFound} />
</Switch>
</Suspense>
</div>
</StylesProvider>
</Router>
);
};
webpack.common.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react', '@babel/preset-env'],
plugins: ['@babel/plugin-transform-runtime'],
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};
webpack.dev.js
const { merge } = require('webpack-merge');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const commonConfig = require('./webpack.common');
const packageJson = require('../package.json');
const devConfig = {
mode: 'development',
output: {
publicPath: 'http://localhost:8080/',
},
devServer: {
port: 8080,
historyApiFallback: {
index: 'index.html',
},
},
plugins: [
new ModuleFederationPlugin({
name: 'container',
remotes: {
marketing: 'marketing@http://localhost:8081/remoteEntry.js',
auth: 'auth@http://localhost:8082/remoteEntry.js',
},
shared: packageJson.dependencies,
}),
],
};
module.exports = merge(commonConfig, devConfig);
marketing: (SUB APPS)
app.js
import React from 'react';
import { Switch, Route, Router } from 'react-router-dom';
import {
StylesProvider,
createGenerateClassName,
} from '@material-ui/core/styles';
import Landing from './components/Landing';
import Pricing from './components/Pricing';
const generateClassName = createGenerateClassName({
productionPrefix: 'ma',
});
export default ({ history }) => {
return (
<div>
<StylesProvider generateClassName={generateClassName}>
<Router history={history}>
<Switch>
<Route path="/pricing" component={Pricing} />
<Route path="/" component={Landing} />
</Switch>
</Router>
</StylesProvider>
</div>
);
};
webpack.common.js
module.exports = {
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react', '@babel/preset-env'],
plugins: ['@babel/plugin-transform-runtime'],
},
},
},
],
},
};
webpack.dev.js
const { merge } = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const commonConfig = require('./webpack.common');
const packageJson = require('../package.json');
const devConfig = {
mode: 'development',
output: {
publicPath: 'http://localhost:8081/',
},
devServer: {
port: 8081,
historyApiFallback: true
},
plugins: [
new ModuleFederationPlugin({
name: 'marketing',
filename: 'remoteEntry.js',
exposes: {
'./MarketingApp': './src/bootstrap',
},
shared: packageJson.dependencies,
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};
module.exports = merge(commonConfig, devConfig);
And below is the package version I have used:
package.json
{
"name": "marketing",
"version": "1.0.0",
"scripts": {
"start": "webpack serve --config config/webpack.dev.js",
"build": "webpack --config config/webpack.prod.js"
},
"dependencies": {
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^5.3.3"
},
"devDependencies": {
"@babel/core": "^7.15.8",
"@babel/plugin-transform-runtime": "^7.15.8",
"@babel/preset-env": "^7.15.8",
"@babel/preset-react": "^7.15.8",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^4.0.0",
"css-loader": "^6.3.0",
"html-webpack-plugin": "^5.3.2",
"style-loader": "^3.3.0",
"webpack": "^5.57.1",
"webpack-cli": "^4.9.0",
"webpack-dev-server": "^4.3.1",
"webpack-merge": "^5.2.0"
}
}
Routing Problems
And another problem in when I add the /
route at the top like this
<Switch>
<Route path="/" component={MarketingLazy} />
<Route path='/auth' component={AuthAppLazy} />
<Route path={"*"} component={NotFound} />
</Switch>
the components which are in the /auth
route is not rendering, in this case only the /
route component is rendering but the path is changing in the browser.
But when I put the /
at the bottom like this, everything works fine
<Switch>
<Route path='/auth' component={AuthAppLazy} />
<Route path="/" component={MarketingLazy} />
<Route path={"*"} component={NotFound} />
</Switch>
How can I resolve these issues.
Upvotes: 3
Views: 358
Reputation: 202874
The issue is that the path="*"
route is effectively unreachable because the "/"
above it also matches any route.
<Switch>
<Route path="/auth" component={AuthAppLazy} />
<Route path="/" component={MarketingLazy} /> // <-- matches "/*"
<Route path="*" component={NotFound} /> // <-- matches "/*" and unreachable
</Switch>
When you moved <Route path="/" component={MarketingLazy} />
to the top of the routes it then also made "/auth"
path unreachable.
The Switch
renders the first child <Route>
or <Redirect>
that matches the location. This means that path order and specificity matters. If the app renders any page on "/"
and also wants to define an even less specific "catch-all" route then it needs to exactly match "/"
then allow for the general fallback.
<Switch>
<Route path="/auth" component={AuthAppLazy} />
<Route path="/" exact component={MarketingLazy} /> // <-- matches "/" exactly
<Route path="*" component={NotFound} /> // <-- matches "/*" and reachable
</Switch>
Upvotes: 1