Reactjs - SPA Routing using a Navbar - reactjs

Problem
I want to start off by saying that I am still new to reactjs and nodejs as a whole. I am working on a dynamic NavBar for a Single Page Application (SPA), but I am struggling with the routing. It took me some time, but I got the routing to work in general. For some reason, the "topics" and "about" pages will load properly (without reloading the whole page); however, the two reports (General Reports and International Reports) Nav options will reload the page with each click, defeating the purpose of a SPA. I looked through tutorial after tutorial just to get this far... I am hoping someone here can assist in pointing out what I am doing wrong.
The Code
This is the Navbar code:
import React from 'react';
import { render } from 'react-dom';
import { Navbar, Nav, NavItem, NavLink, MenuItem, NavDropdown } from 'react-bootstrap';
import { Router, Route, Switch } from 'react-router';
import { getUserGroup } from './Auth.jsx'; /*this is a mock file, the code only assigns a role that I can control by changing a "username"*/
var isDev = getUserGroup() === "dev" ? true : false;
var isAdmin = getUserGroup() === "admin" ? true : false;
var isDataAnalyst = getUserGroup() === "analyst" ? true : false;
/* setup this way intentionally to dynamically render the Navigation bar */
var renderUserVerify = !isDataAnalyst ? <MenuItem eventKey={1.1} href="/UserVerfication">USER VERIFICATION</MenuItem> : null;
var renderPkgLookup = !isDataAnalyst ? <MenuItem eventKey={1.2} href="/PkgInqueryn">PACKAGE INQUERY</MenuItem> : null;
var renderQuerySearch = !isDataAnalyst ? <NavDropdown eventKey={1} title="QUERY SEARCHES" id="query-nav-dropdown">
{renderUserVerify}
{renderPkgLookup}
</NavDropdown> : null;
var renderUpdateUser = isDev || isAdmin ? <NavItem eventKey={2} href="/UpdateUser">UPDATE USER</NavItem> : null;
var renderFPReports = isDev || isAdmin || isDataAnalyst ? <MenuItem eventKey={3.1} href="/reports/GenReports">GENERAL REPORTS</MenuItem> : null;
var renderIntlReports = isDev || isAdmin || isDataAnalyst ? <MenuItem eventKey={3.2} href="/reports/IntlReports">INTERNATIONAL REPORTS</MenuItem> : null;
var renderReports = isDev || isAdmin || isDataAnalyst ? <NavDropdown eventKey={3} title="REPORTS" id="reports-nav-dropdown">
{renderGenReports}
{renderIntlReports}
</NavDropdown> : null;
var renderUserPref = !isDataAnalyst ? <NavItem eventKey={4} href="/UserPref">USER PREFERENCE</NavItem> : null;
export default class Navigation extends React.Component {
handleSelect(eventKey) {
/*event.preventDefault();*/
this.state.href
}
render() {
return (
<div>
<Navbar inverse fluid collapseOnSelect>
<Navbar.Header>
<Navbar.Brand>
CPMS
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
<Nav onSelect={c => this.handleSelect(c)}>
{renderQuerySearch}
{renderUpdateUser
{renderReports}
{renderUserPref}
<NavItem eventKey={5} href="/Help">HELP</NavItem>
<NavItem eventKey={6} href="/logout">LOGOUT</NavItem>
</Nav>
</Navbar.Collapse>
</Navbar>
</div>
);
}
}
This is the routing code:
import React from "react";
import { BrowserRouter as Router, Route, Link, HashRouter, NavLink } from "react-router-dom";
import GenReports from '../pages/GenReports';
import IntlReports from '../pages/IntlReports';
const BodyContent = () => (
<Router>
<div>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} /> /* dummy page for testing routing */
<Route path="/topics" component={Topics} /> /* dummy page for testing routing */
<Route path="/Reports/GenReports" component={GenReports} />
<Route path="/Reports/IntlReports" component={IntlReports} />
</div>
</Router>
);
const Home = () => (
<div>
<h2>This is the home page</h2>
</div>
);
const About = () => (
<div>
<h2>About</h2>
</div>
);
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<NavLink to={`${match.url}/rendering`}>Rendering with React</NavLink><br />
<NavLink to={`${match.url}/components`}>Components</NavLink><br />
<NavLink to={`${match.url}/props-v-state`}>Props v. State</NavLink><br />
<Route path={`${match.url}/:topicId`} component={Topic} />
<Route
exact
path={match.url}
render={() => <h3>Please select a topic.</h3>}
/>
</div>
);
const Topic = ({ match }) => (
<div>
<h3>{match.params.topicId}</h3>
</div>
);
export default BodyContent;
This is the code for the App.js
import React, { Component } from 'react';
import Navigation from './components/NavComponent';
import BodyContent from './components/RoutingComponent';
import 'react-bootstrap';
import logo from './logo.svg';
import './App.css';
export default class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<Navigation />
<BodyContent />
</div>
);
}
}
This is the GenReports page (the other reports page is setup the same way)
import React from 'react';
export default class IntlReports extends React.Component {
render() {
return (
<h1>You have reached the General Reports Page</h1>
);
}
}

Related

Redirections with React

I'm trying to have a Home Page and with differents links redirect to the differents pages of my app.
I used <Routes> and <Route> to redirect them but is not working. It stay in blank.
I want to Navebar be the layout here, so I read that it must contain the other Routes inside of it
import React from 'react';
import { Route, Routes } from 'react-router';
import './App.scss';
import Navbar from './components/Navbar/Navbar'
import Home from './components/Home/index'
import Contact from './components/Contact'
const App = () => {
return (
<>
<Routes>
<Route path='/' element={<Navbar/>}>
<Route exact={true} index element={<Home/>}></Route>
<Route exact={true} path='contact' element={<Contact/>}></Route>
</Route>
</Routes>
</>
);
}
export default App;
index.js
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
This is the Navbar (which is the onlyone is showing)
class Navbar extends Component{
state = { clicked: false}
handleClick = () =>{
this.setState({clicked: !this.state.clicked})
}
render(){
return(
<>
<nav className='navbar-items'>
<img alt='mRadio' className='navbar-logo' href="../Home/index" src={require('../../assets/images/mRadio.png')}></img>
<div className='menu-icon' onClick={this.handleClick}>
<i className={this.state.clicked ? 'fas fa-times' : 'fas fa-bars'}></i>
</div>
<ul className={this.state.clicked ? 'nav-menu active' : 'nav-menu'}>
{MenuItems.map((item, index)=>{
return (
<li key={index}>
<a className={item.cName} href={item.url}>{item.title}</a>
</li>
)
})
}
</ul>
</nav>
</>
)
}
}
And this is the Home page:
const Index = () => {
return (
<div className='main'>
<video src={videomRadio} autoPlay loop muted/>
</div>
);
}
And this is a Third page:
const Index = () => {
return (
<div>
<p>CONTACT PAGE</p>
</div>
);
}
If you re using the latest version 6, don't use Switch, now is become Routes, try to change the path like this for example if is nested component:
import { Route, Routes } from 'react-router-dom'; //just in case
//...
const App = () => {
return (
<>
<Routes>
<Route path='/' element={<Navbar/>}>
<Route index element={<Home/>} />
<Route path='contact' element={<Contact/>}/>
</Route>
</Routes>
</>
);
}
export default App;
create a child in home and third page.that gives access to that page directly.

problem rendering pages using react-router-dom getting warning msg: router.ts:11 No routes matched location "/charterdetails"

I'm new to react and I'm trying to build application to make reservation for fishing charters for a school project I'm working on. I'm using react-router-dom which works for the most part, but when I try and use a button to navigate to page it does not render components on 2 of the pages. The page will refresh but it does not render the components "CharterDetails" or "BookCharter".
I get no errors but I do get a warning that says:
No routes matched location "/charterdetails/62fb097cb985e11cb3884f6e"
It does not seem to matter if I use the button handler or just a link both options give the same result for both CharterDetails and BookCharter, navigation for other pages render as expected.
Click here for my GitHub repository
select the work-in-progress branch
hope I’m explaining clear enough. Thank for your time
App.js:
import React, { useState, useEffect } from "react";
import Nav from "./Components/Nav";
import {
BrowserRouter as Router,
Route,
Routes,
useParams,
} from "react-router-dom";
import Login from "./Components/Users/LoginForm";
import "./App.css";
import Landings from "./Pages/Landings.js";
import Boats from "./Pages/Boats";
import Charters from "./Pages/Charters";
import CharterDetails from "./Pages/CharterDetails";
import BookCharter from "./Pages/BookCharter";
import PageNotFound from "./Pages/404";
import Home from "./Pages/Home";
function App() {
const { id } = useParams();
console.log("useParams", id);
return (
<>
<header>
<img
src="../assets/Images/scsfcBadge.png"
alt="SoCal Sportfishing Club"
/>
SoCal Sportfishing Club
</header>
<div className="main">
<Router>
<aside className="left">
<Nav />
</aside>
<main>
<Routes>
<Route index element={<Home />} />
<Route path="/charters" element={<Charters />} />
<Route path="/landings" element={<Landings />} />
<Route path="/boats" element={<Boats />} />
{/* need to add parameter :id to link */}
<Route page="/bookcharter/" element={<BookCharter />} />
{/* need to add parameter :id to link */}
<Route page="/charterdetails/:id" element={<CharterDetails />} />
<Route page="*" element={<PageNotFound />} />
</Routes>
</main>
<aside className="right">
<Login />
</aside>
</Router>
</div>
<footer>Copyright © 2022 SoCal SportFishing Club </footer>
</>
);
}
export default App;
Home.js:
import React,{useState , useEffect} from 'react';
import { Link, useNavigate } from "react-router-dom";
import { BrowserRouter as Router, Route, Routes, useParams } from "react-router-dom";
function Home(){
const navigate = useNavigate();
const [charterData, setCharterData] = useState([]);
const [charterid, setCharterid] = useState('');
useEffect(() => {
// declare the async data fetching function
const fetchData = async () => {
fetch('http://localhost:5000/charter/featured/')
.then((response) => response.json())
.then((data) => setCharterData(data));
console.log('Charter ', charterData)
}
// call the function
fetchData()
// make sure to catch any error
.catch(console.error);
}, [])
function handleClick(charter) {
let id = charter.id;
let path = `/charterdetails//${id}`
navigate(path);
}
function BookClick(e) {
// e.preventDefault();
let path = `/bookcharter/${e.target.id}`
navigate(path);
}
return(
<div className="container">
{
charterData.map((charter, index) => {
return(<div key={index} className="card">
<div className="card-header">
</div>
<img src={'../assets/Images/charters/' + charter.Img} alt={charter.CharterName} />
<div className="tag tag-teal">{charter.BoatName} - {charter.Duration}</div>
<div className="card-body">
<div style={{fontSize:'28px', fontWeight: 'Bold'}}>
{charter.CharterName}
</div>
<p style={{fontSize:'16px'}}>Departure date: {new Date(charter.Departure).toDateString()}<br />
Return date: {new Date(charter.Return).toDateString()}<br />
Trip Duration: {charter.Duration}<br />
Price: ${charter.Cost}<br />
Max Load:{charter.MaxLoad}<br />
Target: {charter.Target}<br /></p>
<div style={{textAlign:'center', width: '100%'}}>
<Link
to= {{
pathname:`/charterdetails/${charter.id}`
}}
>
<button>Details</button>
</Link>
<button onClick={BookClick}>Book</button>
</div>
</div>
</div>)
})
}
</div>
)
}
export default Home;
Nav.js:
import React from 'react';
import { Link } from "react-router-dom";
export default function Nav() {
return (
<>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/landings">Landings</Link>
</li>
<li>
<Link to="/boats">Boats</Link>
</li>
<li>
<Link to="/charters">Club Charters</Link>
</li>
<li>
<Link to="/charterdetails">Charter Details</Link>
</li>
<li>
<Link to="/bookcharter">Book Charter</Link>
</li>
</ul>
</>
);
}
CharterDetails.js:
import React from 'react';
function CharterDetails(){
return(
<div>
Charter Details
</div>
)
}
export default CharterDetails;
BookCharter.js:
import React from "react";
function BookCharter(){
return(
<>
Book Charter
</>
)
}
export default BookCharter;
In the app.js I had page instead of path on the route.
Changed from
<Route page="/bookcharter" element={<BookCharter />} />
<Route page="/charterdetails/:id" element={<CharterDetails />} />
to
<Route exact path="/bookcharter" element={<BookCharter />} />
<Route exact path="/charterdetails/:id" element={<CharterDetails />} />

React bootstrap Nav.Link only showing as active on second click

I have added a basic react-bootstrap navbar and set the NavLinks up as react-router .Links.
When I click on the links they do not show as active. They will only show as active on the second click of the same link. The router is set up in the app.js file. How do I get them to show as active on first click?
Here is the code for my NavBar component:
import { Navbar, Nav, Container } from 'react-bootstrap'
import { Link } from 'react-router-dom'
import LanguageSwitchSelector from './LanguageSwitchSelector'
import Logout from './Logout'
import { useSelector } from 'react-redux'
import text from '../data/text'
const NavBar = () => {
const currentLanguage = useSelector((state) => state) //retrieves current language from store
if(!currentLanguage) {
return null
}
const code = currentLanguage.code
return (
<div>
<Navbar collapseOnSelect expand="lg" bg="dark" variant="dark">
<Container>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Nav.Link as={Link} to="/dashboard">
{text[code]['dashboard']}
</Nav.Link>
<Nav.Link as={Link} to="/competitions">
{text[code]['competitions']}
</Nav.Link>
<Nav.Link as={Link} to="/tracks">
{text[code]['tracks']}
</Nav.Link>
</Nav>
</Navbar.Collapse>
<LanguageSwitchSelector />
<Logout text={text[code]['logout']} />
</Container>
</Navbar>
</div>
)
}
export default NavBar
App.js
import React, { useState, useEffect } from 'react'
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from 'react-router-dom'
import { useDispatch } from 'react-redux'
import Login from './components/Login'
import Dashboard from './components/Dashboard'
import ManageCompetitions from './components/ManageCompetitions'
import ManageTracks from './components/ManageTracks'
import { initLanguage } from './reducers/languageReducer'
const App = () => {
const [user, setUser] = useState()
const dispatch = useDispatch()
useEffect(() => {
const loggedInUser = localStorage.getItem('user')
const savedLanguage = localStorage.getItem('lang')
if (savedLanguage) {
const foundLanguage = JSON.parse(savedLanguage)
dispatch(initLanguage(foundLanguage))
console.log('found language ', foundLanguage)
} else {
dispatch(initLanguage())
}
if (loggedInUser) {
const foundUser = JSON.parse(loggedInUser)
setUser(foundUser)
console.log(foundUser)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<Router>
<Switch>
<Route path="/login">
<Login />
</Route>
<Route path="/dashboard">
<Dashboard />
</Route>
<Route path="/competitions">
<ManageCompetitions />
</Route>
<Route path="/tracks">
<ManageTracks />
</Route>
<Route path="/">
{user ? <Redirect to="/dashboard" /> : <Redirect to="/login" />}
</Route>
</Switch>
</Router>
)
}
export default App
import React, { useState } from 'react';
import { Navbar, Nav, Container } from 'react-bootstrap';
export default function App() {
const [id, setID] = useState('test1');
const handleActive = (id) => {
setID(id);
};
return (
<div>
<Navbar collapseOnSelect expand="lg" bg="dark" variant="dark">
<Container>
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Nav.Link
onClick={() => handleActive('test1')}
to="/test1"
style={{ color: id === 'test1' ? 'red' : '' }}
>
test1
</Nav.Link>
<br />
<Nav.Link
onClick={() => handleActive('test2')}
id={'test2'}
to="/test2"
style={{ color: id === 'test2' ? 'red' : '' }}
>
test2
</Nav.Link>
<br />
<Nav.Link
onClick={() => handleActive('test3')}
id={'test3'}
to="/test3"
style={{ color: id === 'test3' ? 'red' : '' }}
>
test3
</Nav.Link>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</div>
);
}
You can simple take a state of id which unique the tab or menu you click on and set the state of that menu then in style or class you can use ternary check
please have a look above code or copy and run.

react-boostrap and react-router causing full page reload

I am just trying to get React-Boostrap and React-Router up and running together. I used Create React App to create a simple shell.
This is my code, which does actual work nicely with React Router
import React, { Component } from "react";
import { RouteComponentProps, useHistory } from 'react-router';
import {
BrowserRouter as Router,
Switch,
Route,
useParams,
BrowserRouter
} from "react-router-dom";
import 'bootstrap/dist/css/bootstrap.min.css';
import {
Nav,
Navbar
} from "react-bootstrap";
function Navigation() {
return (
<BrowserRouter >
<div>
<Navbar bg="light" expand="lg">
<Navbar.Brand href="#home">React-Bootstrap</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<Nav.Link href="/">Home</Nav.Link>
<Nav.Link href="/about">About</Nav.Link>
<Nav.Link href="/users/1">/users/1</Nav.Link>
<Nav.Link href="/users/2">/users/2</Nav.Link>
<Nav.Link href="/users2/1">/users2/1</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
{/* A <Switch> looks through its children <Route>s and
renders the first one that matches the current URL. */}
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/users/:id" render={() => <Users />}/>
<Route path="/users2/:id" component={Users2} />
<Route path="/">
<Home />
</Route>
</Switch>
</div>
</BrowserRouter >
);
}
class AppRouterBootstrap extends Component {
render() {
return (
<div id="App">
<Navigation />
</div>
);
}
}
export default AppRouterBootstrap;
function Home() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
function Users() {
// We can use the `useParams` hook here to access
// the dynamic pieces of the URL.
let { id } = useParams();
let history = useHistory();
const handleClick = () => {
history.push("/home");
};
return (
<div>
<h3>ID: {id}</h3>
<button type="button" onClick={handleClick}>Go home</button>
</div>
);
}
class Users2 extends React.Component<RouteComponentProps, any> {
constructor(props: any) {
super(props);
}
render() {
return (
<div>
<h1>Hello {(this.props.match.params as any).id}!</h1 >
<button
type='button'
onClick={() => { this.props.history.push('/users/1') }} >
Go to users/1
</button>
</div>
);
}
}
Which looks like this when rendered. Which looks ok, and actually works (on a navigation, and parameters etc etc point of view just fine)
However what I am noticing is that whenever I click on one of the React-Boostrap nav links, there is a FULL network reload occurring, if I monitor the Network tab. If I do not use React-Boostrap nav, but instead use a simple react-router and some Link from react-router. this full reload is not occurring. It only seems to happen when using React-Boostrap nav links
Does anyone know if this is normal, should React-Boostrap nav when used with React-Router be doing this. I guess its possible since its not using the inbuilt React-Router Link classes, but rather its own Nav based items.
Anyone have any ideas on this one?
The Nav.Link will refresh the pages by default (same functionality as a href).
You need to change it to <Nav.Link as={Link} to="/"> instead of <Nav.Link href="/"> to fix the same where Link is from react-router-dom

React-router v.4 - Links in header doesn't work

I'm using react-router v.4 in my React app and have faced with this trouble: when I click at <Link> component, the URL is changing, but data is not rendering.
Here is my code snippets:
Header.component.js:
import React from 'react';
import Navbar from 'react-bootstrap/lib/Navbar';
import Nav from 'react-bootstrap/lib/Nav';
import NavItem from 'react-bootstrap/lib/NavItem';
import NavDropdown from 'react-bootstrap/lib/NavDropdown';
import MenuItem from 'react-bootstrap/lib/MenuItem';
import {Link} from 'react-router-dom';
//utils
import History from '../../utils/History';
import bindAll from 'lodash/bindAll';
class Header extends React.Component {
constructor(props) {
super(props);
bindAll(this, '_getHeaderItem');
}
_getHeaderItem() {
switch (History.location.pathname) {
case '/sign-in':
return <li><Link to='/sign-up'>Sign Up</Link></li>;
break;
case '/sign-up':
return <li><Link to='/sign-in'>Sign In</Link></li>;
break;
default:
return null;
}
}
render() {
return (
<Navbar collapseOnSelect>
<Navbar.Header>
<Navbar.Brand>
Elevator
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
<Nav>
<NavItem eventKey={1} href="#">Link</NavItem>
</Nav>
<Nav pullRight>
{this._getHeaderItem()}
<NavDropdown eventKey={4} id='member-dropdown' title={
<div className='inline-block'>
<img src='' alt='' />
Username
</div>
}>
<MenuItem eventKey={4.1}>My profile</MenuItem>
<MenuItem eventKey={4.2}>Logout</MenuItem>
<MenuItem divider />
<MenuItem eventKey={4.3}>Administration</MenuItem>
</NavDropdown>
</Nav>
</Navbar.Collapse>
</Navbar>
);
}
}
export default Header;
app.js: (which is collecting all components and has routing logic)
import React from 'react';
import ReactDOM from 'react-dom';
import fetch from 'whatwg-fetch';
import { Router, Route, Redirect } from 'react-router';
//utils
import History from './utils/History';
//components
import App from './common/components/App.component';
import SignUp from './modules/signUp/SignUp.component';
import SignIn from './modules/signIn/SignIn.component';
import Footer from './modules/footer/Footer.component';
//styles
require ('./../styles/main.scss');
let loggedIn = localStorage.getItem('token');
loggedIn ? History.replace('/dashboard') : History.replace('/sign-in');
ReactDOM.render(
<Router history={History}>
<App>
<Route path='/sign-in' render={() => (
loggedIn ? (
<Redirect to="/dashboard" />
) : (
<SignIn />
)
)} />
<Route path='/sign-up' render={() => (
loggedIn ? (
<Redirect to="/dashboard" />
) : (
<SignUp />
)
)} />
</App>
</Router>,
document.getElementById('app'));
ReactDOM.render(<Footer />, document.getElementById('footer'));
Temporary, I've done it with click handling (prevent default behavior and window.location.reload('some/path')), but it's not a React way.
I'll appreciate any advice.

Resources