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

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.

Related

React main page is not loading

I'm trying to develop an online shop in React. Trying to load app.js. I don't get any errors, just a blank page.
App.js as follows:
import AppRouter from "./components/AppRouter";
import NavBar from "./components/NavBar";
const App = observer(() => {
const {user} = useContext(Context)
const [loading, setLoading] = useState(true)
useEffect(() => {
check().then(data => {
user.setUser(true)
user.setIsAuth(true)
}).finally(() => setLoading(false))
}, [])
if (loading) {
return <Spinner animation={"grow"}/>
}
return (
<BrowserRouter>
<NavBar />
<AppRouter />
</BrowserRouter>
);
});
export default App;
NavBar.js as follows:
import {NavLink, useNavigate} from "react-router-dom";
import {ADMIN_ROUTE, LOGIN_ROUTE, SHOP_ROUTE} from "../utils/consts";
const NavBar = observer(() => {
const {user} = useContext(Context)
const navigate = useNavigate()
const logOut = () => {
user.setUser({})
user.setIsAuth(false)
}
return (
<Navbar bg="dark" variant="dark">
<Container>
<NavLink style={{color:'white'}} onClick={SHOP_ROUTE}>Victoria's Tech Store
</NavLink>
{user.isAuth ?
<Nav className="ml-auto" style={{color: 'white'}}>
<Button
variant={"outline-light"}
onClick={() => navigate.push(ADMIN_ROUTE)}
>
Admin
</Button>
<Button
variant={"outline-light"}
onClick={() => logOut()}
className="ml-2"
>
Logout
</Button>
</Nav>
:
<Nav className="ml-auto" style={{color: 'white'}}>
<Button variant={"outline-light"} onClick={() => (LOGIN_ROUTE)}>Login</Button>
</Nav>
}
</Container>
</Navbar>
);
});
export default NavBar;
AppRouter.js as follows:
import React, {useContext} from 'react';
import {BrowserRouter as Routes, Route, Router, useNavigate} from 'react-router-dom'
import {SHOP_ROUTE} from "../utils/consts";
const AppRouter = observer(() => {
const {user} = useContext(Context);
const Navigate = useNavigate();
console.log(user)
return (
<Router>
<Routes>
{user.isAuth && authRoutes.map(({path, Element}) =>
<Route key={path} path={path} element={Element} exact/>
)}
{publicRoutes.map(({path, Element}) =>
<Route key={path} path={path} element={Element} exact/>
)}
<Navigate to={SHOP_ROUTE}/>
</Routes>
</Router>
);
});
export default AppRouter;
Look in the browser console for any error messages, maybe what's causing the issue is some content that isn't being loaded like a file not imported or something along those lines.

React Update Navbar When User Updates Profile

I have a React Application which just contains Authtentication Functionality.
I created a dashboard as a home page which displays users email, and a navbar which checks if user is logged in or not. If user logged in, it displays a dropdown with a title user's email(dropdown items: update profile/log out) else, it just displays a log in button.
In the Update Profile page, User can change password and email.
My problem is, if user chanhges email, navbar do not update itself but dashboard do.
App.js:
import "./style.css";
import Navigation from "./Navbar";
import Signup from "./Signup";
import { AuthProvider } from "../contexts/AuthContext";
import Dashboard from "./Dashboard";
import Login from "./Login";
import Logout from "./Logout";
import ForgotPassword from "./ForgotPassword";
import PrivateRoute from "./PrivateRoute";
import UpdateProfile from "./UpdateProfile";
function App() {
return (
<Router >
<AuthProvider>
<Navigation />
<Routes>
<Route
path="/"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
></Route>
<Route
path="/update-profile"
element={
<PrivateRoute>
<UpdateProfile />
</PrivateRoute>
}
></Route>
<Route path="/signup" element={<Signup />} />
<Route path="/login" element={<Login />} />
<Route path="/logout" element={<Logout />} />
<Route path="/forgot-password" element={<ForgotPassword />} />
</Routes>
</AuthProvider>
</Router>
);
}
export default App;
Navbar.js:
import Container from "react-bootstrap/Container";
import Nav from "react-bootstrap/Nav";
import Navbar from "react-bootstrap/Navbar";
import NavDropdown from "react-bootstrap/NavDropdown";
import { useAuth } from "../contexts/AuthContext";
import { useEffect, useState } from "react";
const Navigation = () => {
const { currentUser } = useAuth();
const [email, setEmail] = useState("");
return (
<div>
<Navbar bg="light" expand="lg">
<Container>
<Navbar.Brand>
<Nav.Link as={Link} to="/">
Commercial
</Nav.Link>
</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav>
<NavDropdown
title="Man"
className="dropdown"
id="basic-nav-dropdown"
renderMenuOnMount={true}
>
<NavDropdown.Item>Shirt</NavDropdown.Item>
<NavDropdown.Item>Jean</NavDropdown.Item>
</NavDropdown>
<NavDropdown
title="Woman"
id="basic-nav-dropdown"
className="dropdown"
renderMenuOnMount={true}
>
<NavDropdown.Item>Dress</NavDropdown.Item>
<NavDropdown.Item>Skirt</NavDropdown.Item>
</NavDropdown>
</Nav>
</Navbar.Collapse>
<Navbar.Collapse
className="justify-content-end"
id="responsive-navbar-nav"
>
<Nav>
{currentUser ? (
<NavDropdown
title={currentUser.email}
className="dropdown"
id="basic-nav-dropdown"
renderMenuOnMount={true}>
<Nav.Link as={Link} to="/update-profile">
Go Profile
</Nav.Link>
<Nav.Link as={Link} to="/logout">
Log Out
</Nav.Link>
</NavDropdown>
) : (
<Nav.Link as={Link} to="/login">
Login
</Nav.Link>
)}
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</div>
);
};
export default Navigation;
Dashboard.js:
import { Card, Button, Alert, Container } from "react-bootstrap";
import { useAuth } from "../contexts/AuthContext";
const Dashboard = () => {
const { currentUser } = useAuth();
return (
<Container className="mt-5 d-flex align-items-center justify-content-center">
<div className="w-100" style={{ maxWidth: "400px" }}>
<Card>
<Card.Body>
<h2 className="text-center mb-4">Profile</h2>
<strong>Email</strong> {currentUser.email}
</Card.Body>
</Card>
</div>
</Container>
);
};
export default Dashboard;
I think I should re-render the navbar every time the page is loaded.
How can I do that, or is there another solutions?
PS:I have not any functional error/bug. Everything works fine.
EDIT: I am sharing other codes, may be this can help:
AuthContext.js:
import React, { useContext, useState, useEffect } from "react";
import { auth } from "../firebase";
const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
const signup = (email, password) => {
return auth.createUserWithEmailAndPassword(email, password);
};
const login = (email, password) => {
return auth.signInWithEmailAndPassword(email, password)
}
const logout = () => {
return auth.signOut();
}
const resetPassword = (email) => {
return auth.sendPasswordResetEmail(email);
}
const updateEmail = (email) => {
return currentUser.updateEmail(email);
}
const updatePassword = (password) => {
return currentUser.updatePassword(password);
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
login,
signup,
logout,
resetPassword,
updateEmail,
updatePassword
};
return (
<AuthContext.Provider value={value}>
<div></div>
{!loading && children}
</AuthContext.Provider>
);
}
PrivateRoute.js:
import { Navigate } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext";
const PrivateRoute = ({ children }) => {
const { currentUser } = useAuth();
return currentUser ? children : <Navigate to="/login" />;
};
export default PrivateRoute;
UpdateProfile.js:
import { Form, Button, Card, Container, Alert } from "react-bootstrap";
import { useAuth } from "../contexts/AuthContext";
import { Link, useNavigate } from "react-router-dom";
const UpdateProfile = () => {
const emailRef = useRef();
const passwordRef = useRef();
const passwordConfirmRef = useRef();
const { currentUser, updateEmail, updatePassword } = useAuth();
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const navigator = useNavigate();
async function handleSubmit(e) {
e.preventDefault();
if (passwordRef.current.value !== passwordConfirmRef.current.value) {
return setError("Passwords do not match");
}
const promises = [];
setLoading(true);
if(emailRef.current.value !== currentUser.email){
promises.push(updateEmail(emailRef.current.value));
}
if(passwordRef.current.value){
promises.push(updatePassword(passwordRef.current.value));
}
Promise.all(promises).then(() => {
navigator("/");
}).catch(() =>{
setError("Failed to update Account");
}).finally(() =>{
setLoading(false);
})
}
return (
<Container className="mt-5 d-flex align-items-center justify-content-center">
<div className="w-100" style={{ maxWidth: "400px" }}>
{error && <Alert variant="danger">{error}</Alert>}
<Card>
<Card.Body>
<h2 className="text-center mb-4">Update Profile</h2>
<Form onSubmit={handleSubmit}>
<Form.Group id="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
ref={emailRef}
defaultValue={currentUser.email}
required
/>
</Form.Group>
<Form.Group id="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
ref={passwordRef}
placeholder="Leave blank to keep the same"
/>
</Form.Group>
<Form.Group id="passwordConfirm">
<Form.Label>Password Confirmation</Form.Label>
<Form.Control
type="password"
ref={passwordConfirmRef}
placeholder="Leave blank to keep the same"
/>
</Form.Group>
<Button disabled={loading} type="submit" className="w-100 mt-3">
Update
</Button>
</Form>
</Card.Body>
</Card>
<div className="w-100 text-center mt-2">
<Link to="/" style={{ textDecoration: "none" }}>
Cancel
</Link>
</div>
</div>
</Container>
);
};
export default UpdateProfile;
I think your Navbar "Navigation" Component is not re-rendering on route change.
You could use redux or context api for state management so that when email updates it is updated in the redux or context api store.
So, your Navbar state should update as it will be taking its value from the redux or context api store and if that value changes the state will update in turn the function will re-render
I am not sure if there are other solutions to this.
From what I can see here it looks like the currentUser object reference is possibly mutated by Firebase. In other words, the currentUser reference in the AuthProvider component only updates when the authentication status changes, but not when user properties like their email are updated.
Since it seems that when changing routes the Dashboard component is rerendered and "sees" the updated currentUser.email value you could create a layout route component that renders the Navigation component.
Example:
import { Navigate, Outlet } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext";
const PrivateRoute = () => {
const { currentUser } = useAuth();
return currentUser ? <Outlet /> : <Navigate to="/login" replace />;
};
...
import { Outlet } from "react-router-dom";
export const Layout = () => (
<>
<Navigation />
<Outlet />
</>
);
function App() {
return (
<Router >
<AuthProvider>
<Routes>
<Route element={<Layout />}> // <-- Navbar renders with routes
<Route element={<PrivateRoute />}>
<Route path="/" element={<Dashboard />} />
<Route path="/update-profile" element={<UpdateProfile />} />
</Route>
<Route path="/signup" element={<Signup />} />
<Route path="/login" element={<Login />} />
<Route path="/logout" element={<Logout />} />
<Route path="/forgot-password" element={<ForgotPassword />} />
</Route>
</Routes>
</AuthProvider>
</Router>
);
}
An alternative might be to add an additional "state" to the AuthProvider component for marking "updates" to trigger rerenders.
Example:
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
const [update, setUpdate] = useState(0);
...
const updateEmail = async (email) => {
const result = await currentUser.updateEmail(email);
setUpdate(c => c + 1);
return result;
}
const updatePassword = async (password) => {
const result = await currentUser.updatePassword(password);
setUpdate(c => c + 1);
return result;
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
login,
signup,
logout,
resetPassword,
updateEmail,
updatePassword
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}

React useEffect renders change on 2nd click?

I have a NavBar component and I want certain items on the NavBar to be left off when one of the other selections is chosen.
It works somewhat. The problem is that the elements that should disappear require a second click.
I want them to disappear from the menu selections with the first click.
I am missing something with useEffect. Am I trying to do something that is a little more complex than my solution is attempting?
Snippet is not working properly
const { React, ReactDOM, PropTypes, w3color } = window
const { useEffect, useState, useRef } = React
const { render } = ReactDOM
import { NavLink } from 'react-router-dom'
const rootNode = document.getElementById('app')
const NavBar = (props) => {
const [menustatus, changeMenusStatus] = useState(null)
const changeContext = async (input) => {
// console.log(input)
changeMenusStatus(input)
}
return (
<>
<h1><ReactLogo />Base React App for Testing Development</h1>
<span>
</span>
<nav>
<NavLink className="navitem" to="/">Home</NavLink>
<NavLink className="navitem" to="/select1">Selection 1</NavLink>
<NavLink className="navitem" to="/select2">Selection 2</NavLink>
{menustatus !== 'restrict' && (
<>
<NavLink className="navitem" to="/select3">Selection 3</NavLink>
<NavLink className="navitem" to="/select4">Selection 4</NavLink>
</>
)
}
<NavLink className="navitem" to="/restrict" onClick={() => changeContext('restrict')}>RESTRICT</NavLink>
<NavLink className="navitem" to="/about">About</NavLink>
</nav>
</>
);
}
const App = () => {
<div className="container">
<NavBar />
</div>
}
render(<App />, rootNode);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.production.min.js"></script>
<div id='app'></div>
The Problem:
The issue is that it takes 2 clicks for the NavBar to re-render without the excluded items. I want the excluded items not to appear when the selected link navigates to the proper page.
NavBar.jsx
import React, { useState, useEffect } from 'react'
import { NavLink } from 'react-router-dom'
import './Nav.css'
import ReactLogo from '../ReactLogo/ReactLogo'
const Nav = (props) => {
const [menustatus, changeMenusStatus] = useState(null)
const NavContext = React.createContext("")
console.log(`[Nav context] = ${props.navcontext}`)
const changeContext = async (input) => {
console.log(input)
changeMenusStatus(input)
}
useEffect(() => {
if (menustatus) {
console.log(menustatus)
} else {
console.log(`menustatus not set.`)
}
}, [menustatus]);
return (
<>
<h1><ReactLogo />Base React App for Testing Development</h1>
<span>
</span>
<NavContext.Provider value={menustatus}>
<nav>
<NavLink className="navitem" to="/">Home</NavLink>
<NavLink className="navitem" to="/select1">Selection 1</NavLink>
<NavLink className="navitem" to="/select2">Selection 2</NavLink>
<NavContext.Consumer>
{(context) =>
(context ==='restrict') ? null
:
<>
<NavLink className="navitem" to="/select3">Selection 3</NavLink>
<NavLink className="navitem" to="/select4">Selection 4</NavLink>
</>
}
</NavContext.Consumer>
<NavLink className="navitem" to="/restrict" onClick={() => changeContext('restrict')}>RESTRICT</NavLink>
<NavLink className="navitem" to="/about">About</NavLink>
</nav>
</NavContext.Provider>
</>
);
}
export default Nav;
App.js
import React, { Component } from 'react';
import { Routes, Route } from 'react-router-dom';
import './App.css';
import Home from './screens/Home/Home'
import About from './screens/About/About';
import ScreenOne from './screens/ScreenOne/ScreenOne'
import ScreenTwo from './screens/ScreenTwo/ScreenTwo'
import ScreenThree from './screens/ScreenThree/ScreenThree'
import ScreenFour from './screens/ScreenFour/ScreenFour'
import ScreenFive from './screens/ScreenFive/ScreenFive'
import PersonList from './components/PersonList/PersonList'
class App extends Component {
state = {people: []}
render() {
return (
<div className="App">
<Routes>
<Route path="/" element={<Home navcontext="one" />} />
<Route path="/about" element={<About navcontext="one" />} />
<Route path="/select1" element={<ScreenOne navcontext="one" />} />
<Route path="/select2" element={<ScreenTwo navcontext="two" />} />
<Route path="/select3" element={<ScreenThree navcontext="three" />} />
<Route path="/select4" element={<ScreenFour navcontext="four" />} />
<Route path="/restrict" element={<ScreenFive navcontext="restrict" />} />
</Routes>
<PersonList people={this.state.people} />
</div>
);
}
}
export default App;
I think the issue is that you are declaring the NavContext context each render cycle inside the Nav component. If you need to use a React Context then be sure to declare it outside any React component.
I don't see the point in using a React Context here though when you can just conditionally render the menu items on the menustatus state locally.
Example:
const Nav = (props) => {
const [menustatus, changeMenusStatus] = useState(null);
const changeContext = (input) => {
console.log(input);
changeMenusStatus(input);
};
useEffect(() => {
if (menustatus) {
console.log(menustatus);
} else {
console.log(`menustatus not set.`);
}
}, [menustatus]);
return (
<>
<h1>
<ReactLogo />
Base React App for Testing Development
</h1>
<nav>
<NavLink className="navitem" to="/">Home</NavLink>
<NavLink className="navitem" to="/select1">Selection 1</NavLink>
<NavLink className="navitem" to="/select2">Selection 2</NavLink>
{menustatus !== 'restrict' && (
<>
<NavLink className="navitem" to="/select3">Selection 3</NavLink>
<NavLink className="navitem" to="/select4">Selection 4</NavLink>
</>
)}
<NavLink className="navitem" to="/restrict" onClick={() => changeContext('restrict')}>RESTRICT</NavLink>
<NavLink className="navitem" to="/about">About</NavLink>
</nav>
</>
);
}

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

Reactjs - SPA Routing using a Navbar

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>
);
}
}

Resources