react-router: whole DOM disappears when inserting NavLink - javascript

I'm new to react and trying to manage the navigation in my web app with react-router.
I must be doing something wrong though, because my whole DOM is disappearing. Everything's fine while I set up the router:
Container.js
import React, {Component} from 'react';
import SideBar from "./sidebar/SideBar";
import Pagina from "./content/Pagina";
import {
BrowserRouter as Router,
NavLink
} from 'react-router-dom'
import NavRoutes from "./NavRoutes";
class Container extends Component {
render() {
return (
<div id="MyLearningContainer" className ="container-fluid">
<SideBar/>
<Pagina/>
<Router>
{NavRoutes}
</Router>
</div>
);
}
}
export default Container;
NavRoutes.js
import React from 'react';
import {
Route
} from 'react-router-dom';
import Dashboard from "./content/Dashboard";
import Utenti from "./content/Utenti";
const navRoutes = (
<Route path="/" component= {Dashboard}>
<Route path="utenti" component = {Utenti} />
</Route>
);
export default navRoutes;
The problem arises when I make my SideBarItem generate a <NavLink> inside its usual <li>:
SideBarItem.js
import React, {Component} from 'react';
import {
NavLink
} from 'react-router-dom'
const defaultClass = {
color: '#00338D',
paddingTop: 10,
paddingBottom: 10,
paddingLeft: 20,
paddingRight: 20
};
const activeClass = {
color: '#fff',
backgroundColor: '#428bca',
paddingTop: 10,
paddingBottom: 10,
paddingLeft: 20,
paddingRight: 20
};
class SideBarItem extends Component {
render() {
return (
<li>
<NavLink to={("/" + this.props.title).toLowerCase()}>
<span className={this.props.glyph}></span> {this.props.title}
</NavLink>
</li>
);
}
}
SideBarItem.defaultProps = {
title: 'Undefined',
glyph: 'glyphicon-home'
}
export default SideBarItem;
When I save this, the whole DOM just disappears and nothing gets rendered anymore!
Update
I edited the Container.js so that SideBar is now a child of Router.
class Container extends Component {
render() {
return (
<div id="MyLearningContainer" className="container-fluid">
<Router>
<div>
<SideBar/>
{NavRoutes}
</div>
</Router>
</div>
);
}
}
Now the DOM renders just fine, but clicking on the NavLink won't make the route change: it stays on Dashboard...

I managed to get the router working!
Following thenormalsquid's advice, I edited the Container.js so that SideBar became a child of Router.
class Container extends Component {
render() {
return (
<div id="MyLearningContainer" className="container-fluid">
<Router>
<div>
<SideBar/>
{NavRoutes}
</div>
</Router>
</div>
);
}
}
Then I edited NavRoutes.js, that wasn't pointing at the right links.
import React from 'react';
import {
Route
} from 'react-router-dom';
import Dashboard from "./content/Dashboard";
import Utenti from "./content/Utenti";
const navRoutes = (
<div>
<Route path="/dashboard" component= {Dashboard}/>
<Route path="/utenti" component = {Utenti} />
<Route path="/corsi" component = {Corsi} />
</div>
);
export default navRoutes;
Now everything works fine and the page changes when you click on the sidebar links!

Related

How to prevent components from unmounting when path changes?

Is there a way to stop the route from unmounting? In my app I would need to keep react state between switching the routes.
Here you can see that, if we change route then home respective route component will mount and unmount. I don't want.
CODESANDBOX DEMO
I've looked in
How can I prevent the unmount in React Components?
How can I prevent React from unmounting/remounting a component?
but doesn't work for me.
index.js
import React from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import App from "./App";
import First from "./routes/First";
import Second from "./routes/Second";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<BrowserRouter>
<Routes>
<Route path="" element={<App />} />
<Route path="first" element={<First />} />
<Route path="second" element={<Second />} />
</Routes>
</BrowserRouter>
);
App.js
import * as React from "react";
import { Link } from "react-router-dom";
export default function App() {
return (
<div>
<h1>Prevent Routing</h1>
<nav
style={{
borderBottom: "solid 1px",
paddingBottom: "1rem"
}}
>
<Link to="/first">First</Link> |<Link to="/second">Second</Link>
</nav>
</div>
);
}
First.js
import * as React from "react";
import { useEffect } from "react";
import { Link } from "react-router-dom";
export default function First() {
useEffect(() => {
console.log("mounted First");
return () => console.log("unmounted First");
}, []);
return (
<main style={{ padding: "1rem 0" }}>
<Link to="/">Home</Link>
<h2>First</h2>
</main>
);
}
Second.js
import * as React from "react";
import { useEffect } from "react";
import { Link } from "react-router-dom";
export default function Second() {
useEffect(() => {
console.log("mounted Second");
return () => console.log("unmounted Second");
}, []);
return (
<main style={{ padding: "1rem 0" }}>
<Link to="/">Home</Link>
<h2>Second</h2>
</main>
);
}
React Router will always unmount the component when it's not matched by a path.
You could fix this by combining both Components with a double route, each component can determine what he will render based on the current path.
So the main routing will look something like:
<Route path={[ '/first', '/second' ]}>
<First {...{}} />
<Second {...{}} />
</Route>
Then use useLocation in the component to toggle the render:
import { useLocation } from "react-router-dom";
function First(props) {
const location = useLocation();
if (location.pathname !== '/first') {
return null
}
}

React Router doesn't render

i've been following an online course about react but it's a bit dated and i've been struggling through all the lessons to understand the concepts explained while applying the new syntax instead of the the one shown in the videos. i've always more or less managed but now i'm stuck because, after adding the react router dom web app, my pages don't render anymore. no errors nor warnings are shown and no matter how much i look, i can't seem to find the mistake. my main right now looks like this:
import React from 'react';
import Header from './header';
import MyPlacesList from './myplaceslist.js';
import Footer from './footer';
import CreatePlace from './createPlace.js';
import * as attAPI from '../utils/attractionsAPI';
import { Router, Route } from 'react-router-dom';
class Main extends React.Component{
state = {
attractions: [],
}
componentDidMount(){
attAPI.getAll().then((attractions) => {
this.setState({attractions})
})
}
removePlace = (attraction) => {
this.setState((state) => ({
attractions: state.attractions.filter((attr) => attr.id !== attraction.id)
}))
attAPI.remove(attraction);
}
render(){
return (
<Router>
<Fragment>
<Header titolo = 'My Places' sottotitolo = 'I miei posti preferiti'/>
<Route exact path = '/' render = {() => (
<MyPlacesList attractions = {this.state.attractions}
onRemovePlace = {this.removePlace} />
)}/>
<Route path = '/create' render = {() => (
<CreatePlace />
)}/>
<Footer />
</Fragment>
</Router>
)
}
}
export default Main;
and this is the header:
import React from 'react';
import AppBar from '#mui/material/AppBar';
import Box from '#mui/material/Box';
import Toolbar from '#mui/material/Toolbar';
import Typography from '#mui/material/Typography';
import IconButton from '#mui/material/IconButton';
import Icon from '#mui/material/Icon';
import AddCircleIcon from '#mui/icons-material/AddCircle';
import { Link } from 'react-router-dom';
class Header extends React.Component{
render(){
// return <nav>
// <div className = "nav-wrapper">
// {this.props.titolo}
// </div>
// </nav>
return <Box sx={{ flexGrow: 1 }}>
<AppBar position="static">
<Toolbar>
<Typography
variant="h6"
noWrap
component="div"
sx={{ flexGrow: 1, display: { xs: 'none', sm: 'block' } }}
>
{this.props.titolo}
</Typography>
<Link to = '/create'>
<IconButton>
<AddCircleIcon />
</IconButton>
</Link>
</Toolbar>
</AppBar>
</Box>
}
}
export default Header;
before adding react router dom i was using the state to call the components MyPlacesList/CreatePlace (createPlace only gets shown when i click on a button) and it worked just fine, the problem appeared after i tried to use <Route>. any ideas in how could i fix this? thank you in advance!
EDIT - here's my index page too:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Main from './components/main';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<Main/>
</BrowserRouter>, document.getElementById('root'));
Things to correct
Use React.Fragment instead of just Fragment or import from react.
Add All Routes between Routes component.
You have already add Router or BrowserRouter in index.js so don't need to add again.
There is a change in nesting in version 5.
So ultimately the nesting should be in version 6
<BrowserRouter>
<Routes>
<Route>
<Route>
</Routes>
</BrowserRouter>
import MyPlacesList from "./myPlacesList.js";
import CreatePlace from "./createPlace.js";
import { Route, Routes } from "react-router-dom";
class App extends React.Component {
render() {
return (
<React.Fragment>
<div>Some Header component</div>
<Routes>
<Route
exact
path="/"
render={() => (
<MyPlacesList
attractions={this.state.attractions}
onRemovePlace={this.removePlace}
/>
)}
/>
<Route path="/create" render={() => <CreatePlace />} />
</Routes>
</React.Fragment>
);
}
}
export default App;

Page contains from many component turn blank when reload page?

I'm a newbie in React js and currently i'm building a layout that contains from many components
For example, a home page layout that contains sidebar component, topbar component, footer component and main content component.
When i click on a link in sidebar component, the main content will be changed based on url of link i clicked on and other components will be same on all pages regardless of url.
Here is my code:
MainLayout.js
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import Sidebar from '../components/Sidebar';
const MainLayout = ({children, ...rest}) => {
return (
<div className="page page-dashboard">
{/* <div className="sidebar"><Sidebar></Sidebar></div> */}
<div className="main">
{children}
</div>
</div>
)
}
const MainLayoutRoute = ({component: Component, ...rest}) => {
return (
<Route {...rest} render={matchProps => (
<MainLayout>
<Component {...matchProps} />
</MainLayout>
)} />
)
};
export default MainLayoutRoute;
MainPage.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Redirect, Switch } from 'react-router-dom';
import Sidebar from './Sidebar';
import Header from './Header';
import Footer from './Footer';
import UsersList from './UsersList';
export default class MainPage extends Component {
render() {
return (
<Router>
<div>
<Header/>
<Sidebar/>
<div className="wrapper">
<Switch>
<Route path={"/users"} component={UsersList} />
</Switch>
</div>
<Footer></Footer>
</div>
</Router>
);
}
}
Sidebar.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Redirect, Switch, Link } from 'react-router-dom';
export default class Sidebar extends Component {
render() {
return (
<div className="sidenav">
<ul>
<li><Link to="/users">Users</Link></li>
<li><Link to="/settings">Setting</Link></li>
</ul>
</div>
);
}
}
UserList.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Redirect, Switch, Link } from 'react-router-dom';
export default class UsersList extends Component {
render() {
return (
<div>
<button><Link to="/newuser">Add new user</Link></button>
</div>
);
}
}
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Redirect, Switch } from 'react-router-dom';
import logo from './logo.svg';
import './App.css';
import './css/custom.scss';
/** Layouts **/
import LoginLayoutRoute from "./layouts/LoginLayout";
import MainLayoutRoute from "./layouts/MainLayout";
/** Components **/
import MainPage from './components/MainPage';
import LoginPage from './components/LoginPage'
export default class App extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path="/">
<Redirect to="/login" />
</Route>
<LoginLayoutRoute path="/login" component={LoginPage} />
<MainLayoutRoute path="/home" component={MainPage} />
</Switch>
</Router>
);
}
}
But when i refresh the page, it turned blank like this:
Before i refresh page
After i refresh page
How can i fix this ?

I have two different headers because of Router

So the problem is that I'm new to REACT, I used create-react-app and added a Router function to route between components. Now I created a header which everything was okay with, but I added a hamburger-menu so I could route between my pages and suddenly my Header just got itself duplicated on my webpage.
App.js:
import React, {Component} from 'react';
import { BrowserRouter as Router, Route} from "react-router-dom";
import './App.css';
import './Header.css'
import SideDrawer from "./SideDrawer";
import Header from './Header'
import Backdrop from './Backdrop'
import Home from "./Home";
import LoginPage from "./LoginPage";
import SignupPage from "./SignupPage";
import RegisterEventPage from "./RegisterEventPage";
class App extends Component {
state = {
sideDrawerOpen: false
};
drawerToggleClickHandler = () => {
this.setState((prevState) => {
return {sideDrawerOpen: !prevState.sideDrawerOpen};
});
};
backDropClickHandler = () => {
this.setState({sideDrawerOpen: false});
};
render() {
let sideDrawer;
let backdrop;
if (this.state.sideDrawerOpen) {
sideDrawer = <SideDrawer/>;
backdrop = <Backdrop click={this.backDropClickHandler}/>;
}
return (
<div style={{height: '100%'}}>
<Header drawerClickHandler={this.drawerToggleClickHandler}/>
{sideDrawer}
{backdrop}
<Router>
<Route exact path="/" component={Home}/>
<Route path="/loginPage" component={LoginPage}/>
<Route path="/SignupPage" component={SignupPage}/>
<Route path="/RegisterEventPage" component={RegisterEventPage}/>
</Router>
</div>
);
}
}
export default App;
Header.jsx:
import React, {Component} from 'react';
import DrawerToggleButton from './DrawerToggleButton';
import './Header.css';
class Header extends Component {
render() {
return (
<header className="main_toolbar">
<nav className="toolbar_navigation">
<div>
<DrawerToggleButton click={this.props.drawerClickHandler}/>
</div>
<div className="toolbar_logo">IceBreaker</div>
<div className="spacer"></div>
</nav>
</header>
);
}
}
export default Header;
So if I for example remove from App.js my second header suddenly disappears.
Image of two headers
Render your header inside the router:
I also recomend you to use a switch in your router
You will need to import it :
import { BrowserRouter as Router, Route} from "react-router-dom";
return (
<div style={{height: '100%'}}>
<Router>
<Header drawerClickHandler={this.drawerToggleClickHandler}/>
{sideDrawer}
{backdrop}
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/loginPage" component={LoginPage}/>
<Route path="/SignupPage" component={SignupPage}/>
<Route path="/RegisterEventPage" component={RegisterEventPage}/>
</Switch>
</Router>
</div>
);

React App not routing to the link

I am new to React and having issues with router. I am just learning from this tutorial: https://medium.com/#thejasonfile/basic-intro-to-react-router-v4-a08ae1ba5c42
Below is the code that is being called from my index.html. When I click on the link 'Show the list', the url changes from localhost:8080 to localhost:8080/list but doesn't really change the context of the page. I am not sure what is going or what I am doing wrong here. Any ideas?
Scripts.js
import React from 'react';
import ReactDOM from 'react-dom';
import {Title, App} from './Components/App';
import { BrowserRouter as Router, Route } from 'react-router-dom';
ReactDOM.render(
<Router>
<div>
<Route exact path="/" component={Title} />
<Route path="/list" component={App} />
</div>
</Router>
, document.getElementById('app'));
App.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
const Title = () => {
return (
<div className="title">
<h1>React Router demo</h1>
<Link to="/list">
<button>Show the List</button>
</Link>
</div>
)};
const List = () => {
return (
<div className="nav">
<ul>
<li>list item</li><li>list item</li></ul><Link to="/"><button>Back Home</button></Link></div>)
}
module.exports = {
Title,
List
};
I refactored your code a bit, you should not render the App component for a single page rather your app component should have all the routes like how I made it below. Then as needed add Link throughout your components when you need to navigate and then add the routes in App respectively.
import React, { Component } from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import { Link } from 'react-router-dom';
class App extends Component {
constructor() {
super();
}
render() {
return (
<Router>
<div>
<Route exact path='/home' render={()=> <Title />} > </Route>
<Route exact path='/list' render={() => <List />} > </Route>
</div>
</Router>
);
}
}
const Title = () => {
return (
<div className="title">
<h1>React Router demo</h1>
<Link to="/list">
<button>Show the List</button>
</Link>
</div>
)};
const List = () => {
return (
<div className="nav">
<ul>
<li>list item</li><li>list item</li></ul><Link to='/home'><button>Back Home</button></Link></div>)
}
render(<App />, document.getElementById('app'));
document.getElementById is usually root looks like you changed it to app which is fine.

Categories