I'm trying to create a React app with multiple pages with React-Router V6. There seems to be an issue with the paths. Whenever I try running the code in my browser, any other path besides the root ("/"), returns a 404. Meaning that it doesn't exist. Here are snippets of the files I'm using:
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './components/App.js';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
App.js
import React from 'react';
// Components
import NavBar from './NavBar';
import Router from './Router';
const App = () => {
return (
<div id='app'>
<Router />
</div>
)
}
export default App;
Router.js
import React from 'react';
import { Routes, Route, Link } from 'react-router-dom';
// Components
import Login from './Login';
import Checkout from './Checkout';
import History from './History';
import Home from './Home';
const Router = () => {
return (
<Routes>
<Route path='/' element={<Home />} />
<Route path='login' element={<Login />} />
<Route path='/history' element={<History />} />
<Route path='/checkout' element={<Checkout />} />
</Routes>
);
}
export default Router;
Home.js
import React from 'react';
import NavBar from './NavBar';
const Home = () => {
return (
<div id='home'>
<NavBar />
<h1>Homepage</h1>
</div>
);
}
export default Home;
Every other component's jsx code <Login>, <History>
etc.. is identical to the <Home>
one, other than the text in the <h1>
tag
This is what happens when I open the root page /
This is what happens when I open any other endpoint /checkout /login
If this isn't enough information.... I can't make a minimal reproducible example as it takes too many files to create an up and running fullstack javascript application. But I can give you the link to the repo and some instructions on how to reproduce the issue. link: https://github.com/Lawsan92/pizzaOrder
- Fork and clone the repo
- open the file in any IDE
- Navigate to the server.js file and replace process.env.PORT with any port 3000 (or anything else)
- in the root directory run
npm run server
- Open whichever browser you use and go to localhost:3000
- The test the other endpoints localhost:3000/login localhost:3000/checkout etc..
Here is my webpack config:
const path = require('path');
require('dotenv').config();
const clientCofig = {
mode: 'development',
entry: {
path: path.join(__dirname, './client/src/index.js')
},
output: {
path: path.join(__dirname, './client/dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /nodeModules/,
loader: 'babel-loader'
},
{
test:/\.css$/,
loader: 'css-loader'
}
]
}
}
module.exports = clientCofig;
CodePudding user response:
Your webpack configuration needs to redirect all page requests to the root index.html file. You can do this by specifying a public path in the output and adding historyApiFallback: true,
in the configuration. You'll also need to update the server.js
file to handle nested page requests.
webpack.config.js
const path = require('path');
require('dotenv').config();
const clientCofig = {
mode: 'development',
entry: {
path: path.join(__dirname, './client/src/index.js')
},
output: {
path: path.join(__dirname, './client/dist'),
filename: 'bundle.js',
publicPath: '/' // <-- add public path here
},
devServer: {
historyApiFallback: true // <-- add fallback here
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /nodeModules/,
loader: 'babel-loader'
},
{
test:/\.css$/,
loader: 'css-loader'
}
]
}
}
module.exports = clientCofig;
server.js
const express = require('express');
const path = require('path');
const app = express();
require('dotenv').config();
app.get('/test', (req, res) => {
res.status(200).send('hello world');
})
app.get('https://order-pizza-api.herokuapp.com/api/orders', (req, res) => {
console.log('res.body:', res.body);
})
app.use(express.static(path.join(__dirname, '../client/dist')))
app.get('/*', function(req, res) { // <-- add
res.sendFile(path.join(__dirname, '../client/dist/index.html'), function(err) {
if (err) {
res.status(500).send(err)
}
})
})
app.listen(process.env.PORT, () => {
console.log(`listening to port: ${process.env.PORT}`)
})
app.use(express.json());
The other issue I found was in your NavBar
component. It's rendering raw anchor tags <a>
instead of the Link
component from react-router-dom
. Import the Link
component and replace all anchor tags with the Link
component and switch the href
attribute to the to
prop.
/src/components/NavBar.js
import React from 'react';
import { Link } from 'react-router-dom';
const NavBar = () => {
return (
<div className='nav'>
<Link to='/' className='site-title'>
Site Title
</Link>
<ul>
<li className='active'>
<Link to='/pricing'>Pricing</Link>
</li>
<li>
<Link to='/history'>About</Link>
</li>
</ul>
</div>
);
};
export default NavBar;