Same component used in multiple routes is being remounted react-router - javascript

I created a simple react application. It has a header and three other components called welcome, feature 1 and feature 2.
index.js
import React from 'react'
import { render } from 'react-dom'
import { BrowserRouter, Route } from 'react-router-dom'
import App from './App';
render((
<BrowserRouter>
<Route path="/" component={App} />
</BrowserRouter>
), document.getElementById('root'));
App.js
import React from 'react'
import Header from './Header'
import Main from './Main'
const App = () => (
<div>
<Header />
<Main />
</div>
)
export default App
Header.js
import React, { Component } from 'react';
export default class Header extends Component {
componentDidMount() {
APICall('/user')
}
render() {
return (
<div>I am header</div>
)
}
}
Main.js
import React, { Component } from 'react'
import { Route } from 'react-router-dom'
import Welcome from './Welcome'
import Feature1 from './Feature1'
import Feature2 from './Feature2'
export default class Main extends Component {
render() {
return (
<div>
<Route exact path="/" component={Welcome} />
<Route path="/feature1" component={Feature1} />
<Route path="/feature2" component={Feature2} />
</div>
)
}
}
welcome.js
import React, { Component } from 'react';
export default class Welcome extends Component {
render() {
return (
<div>Welcome!</div>
)
}
}
Feature1.js
import React, { Component } from 'react';
export default class Feature1 extends Component {
render() {
return (
<div>I am Feature1</div>
)
}
}
Feature2.js
import React, { Component } from 'react';
export default class Feature2 extends Component {
render() {
return (
<div>I am Feature2</div>
)
}
}
Welcome, Feature1 and Feature2 are in different routes where as Header is common in all the routes. Say I have a user and I want to show the username on the header. I will make an API call to get the username in componentDidMount() life-cycle hook of header.
Now if I change the route, I don't want the API call to be made again as the username is not going to change. And I thought that is how this was going to behave. As Header component is same in all the routes, I thought Header won't re-mount when I change the route. But that is not what is happening. It is remounting and making the API call again. How can I make sure that the API call is made only once?

I think in this case considering your Header is aware of what User is logged, i.e. App.js state passed down as props, you could use shouldComponentUpdate():
shouldComponentUpdate(nextProps, nextState) {
// see if user changed
if(nextProps.user !== nextState.user){
return true;
}
// returning false will prevent re-rendering
return false;
}
I hope this is somehow useful.
Have a good day!

change to this in index.js:-
<BrowserRouter>
<App />
</BrowserRouter>

It seems you messed up the routing then, because in your router you have only one route registered, which seems to trigger on any child route change. Also, routes should be wrapped in a Switch component.
Check out this structure
/** index.js */
import React from 'react'
import { render } from 'react-dom'
import App from './App';
render(<App />, document.getElementById('root'));
/** App.jsx */
import React from 'react'
import Header from './Header'
import Main from './Main'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
const App = () => (
<div>
<Header />
<BrowserRouter>
<Switch>
<Route exact path="/" component={Homepage} />
<Route exact path="/f1" component={Feature1} />
<Route exact path="/f2" component={Feature2} />
<Route component={NotFound} />
</Switch>
</BrowserRouter>
</div>
)
export default App

Related

White blank pages in my react application after using the react-router-dom to link pages

I have been receiving white blank pages when trying to create multiple pages within my app and I have been using the router-dom to try and fix this but still can't understand why. Here is my code with Home and Navigation js being inside a components folder in the src directory and App.js just inside the src directory.
App.js
import React, { Component } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Home from './components/Home';
import Glazing from './components/Glazing';
import Navigation from './components/Navigation';
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Navigation />
<Route path="/" component={Home} exact/>
<Route path="/glazing" component={Glazing}/>
</div>
</BrowserRouter>
);
}
}
export default App;
Nav.js
import React from 'react';
import { NavLink } from 'react-router-dom';
const Navigation = () => {
return (
<div>
<NavLink to="/">Home</NavLink>
<NavLink to="/glazing">Glazing</NavLink>
</div>
);
}
export default Navigation;
Home.js
import React from "react";
import logo from '../logo.svg';
import './Home.css';
import "#fontsource/dm-sans";
function home() {
return (
<div className="Home">
<header className="Home-header">
<h1>EPC RATING PREDICTOR</h1>
</header>
<button> GET STARTED</button>
</div>
);
}
export default Home;
If you are using react-router-dom#6 then there are a couple things you need to address.
The Switch component was replaced by the Routes component and all Route components must be wrapped/rendered directly by Routes, or another Route component in the case of nesting routes.
The Route component API changed; gone are the component, and render and children function props, all replaced by a single element prop taking a ReactNode, a.k.a. JSX, value.
Example:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './components/Home';
import Glazing from './components/Glazing';
import Navigation from './components/Navigation';
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/glazing" element={<Glazing />} />
</Routes>
</div>
</BrowserRouter>
);
}
}
See the Upgrading from v5 guide for other changes.

Why is React Router v6 not rendering my components?

I know this question has been asked a lot and I've looked at quite a lot of answers and articles including one on how to upgrade from React Router V5 to V6 since I'm used to V5. However, V6 is giving me a weird issue which I don't know how to fix or what am I doing wrong.
Here's my code below
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Login from './components/dashboard/Login';
import Profile from './components/dashboard/Profile';
import './resources/style/tutorApp.css';
export default class App extends Component {
static displayName = App.name;
render () {
return (
<Router>
<Routes>
<Route path="/" element={ <Profile /> } />
<Route path="/Login" element={ <Login /> } />
</Routes>
</Router>
);
}
}
Profile.js
import React, { Component } from 'react';
class Profile extends Component {
render() {
return (
<div>
<h1>This is my Profile Page.</h1>
</div>
);
}
}
export default Profile;
Login.js
import React, { Component } from 'react';
class Login extends Component {
render() {
return (
<div>
<h1>This is my Login page.</h1>
</div>
);
}
}
export default Login;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
const rootElement = document.getElementById('root');
ReactDOM.render(
<BrowserRouter basename={baseUrl}>
<App />
</BrowserRouter>,
rootElement);
registerServiceWorker();
I just get a blank window in the browser. Nothing is rendered!
What is the problem?
You are mounting a router inside another router.
In ReactDom.render you're wrapping your app in BrowserRouter.
import { BrowserRouter } from 'react-router-dom';
// ...
ReactDOM.render(
<BrowserRouter basename={baseUrl}> // <-- this is the outer browser router
<App />
</BrowserRouter>,
rootElement
);
However inside your app you mount another BrowserRouter in your render method.
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
// ...
render () {
return (
<Router> // <-- this is the nested browser router
<Routes>
<Route path="/" element={ <Profile /> } />
<Route path="/Login" element={ <Login /> } />
</Routes>
</Router>
);
}
To fix the problem simply remove one or the other.
The rest of your code looks fine.

React app routing doesn't load content unless page reloaded

I've been working on a react single page app and have been trying to get the routing to work.
I believe the routing itself actually works however the page does not load the correct content unless the page is manually reloaded. Back and forward browser arrows also work.
For example I can navigate to localhost:3000 fine and the content is loaded correctly but when I press a button to navigate to localhost:3000/contacts nothing is displayed unless I refresh the page. Once manually refreshed the contacts page shows up. What gives?
index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router } from 'react-router-dom';
// Import App component
import App from './App'
// Import service workers
import * as serviceWorker from './serviceWorker'
// Render App component in the DOM
ReactDOM.render(
<Router>
<App />
</Router>
, document.getElementById('root')
)
serviceWorker.unregister()
App.tsx
// Import necessary dependencies
import React from 'react'
import Routes from './Routes'
// Create App component
function App() {
return (
<div className="App">
<Routes />
</div>
)
}
export default App
history.tsx
import { createBrowserHistory as history} from 'history';
export default history();
Home/Home.tsx
import React, { Component } from "react";
import history from './../history';
import "./Home.css";
export default class Home extends Component {
render() {
return (
<div className="Home">
hello home
<button onClick={() => history.push('/Contact')} value='click here'>Get Started</button>
</div>
);
}
}
Contact/Contact.tsx
import React, { Component } from 'react';
class Contact extends Component {
render() {
return (
<div>
hello world
</div>
);
}
}
export default Contact;
Routes.tsx
import React, { Component } from "react";
import {BrowserRouter, Router, Switch, Route } from "react-router-dom";
import Contact from "./Contact/Contact";
import Home from "./Home/Home"
import history from './history'
export default class Routes extends Component {
render() {
return (
<div>
<Router history={history}>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/Contact" component={Contact} />
</Switch>
</Router>
</div>
)
}
}
Any help greatly appreciated
I think there's some extra code that might be causing conflict. You're defining the Router from react-router-dom twice:
Once here, in index.tsx
ReactDOM.render(
<Router> // here
<App />
</Router>
, document.getElementById('root')
)
and then again in Routes.tsx
<Router history={history}> // here
<Switch>
<Route path="/" exact component={Home} />
<Route path="/Contact" component={Contact} />
</Switch>
</Router>
You have to drop one of them, they're probably conflicting each other
Update
In addition to that, I think you should not use the history object directly from your export, but access it through the HOC withRouter. Then, you'd wrap
So you'd do something like this
import React, { Component } from "react";
import { withRouter } from 'react-router-dom'
import "./Home.css";
class Home extends Component {
const { history } = this.props
render() {
return (
<div className="Home">
hello home
<button onClick={() => history.push('/Contact')} value='click here'>Get Started</button>
</div>
);
}
}
export default withRouter(Home)
I think the issue here is that you need to wrap your pages with withRouter() like so:
import React, { Component } from "react";
import history from './../history';
import "./Home.css";
import { withRouter } from 'react-router-dom'; //<---------- add this
class Home extends Component {
render() {
return (
<div className="Home">
hello home
<button onClick={() => history.push('/Contact')} value='click here'>Get Started</button>
</div>
);
}
}
default export withRouter(Home); //<-------------- and add this
You will need to do the same on your Contact page as well.
Why do you have two routers?
I guess you simply need to remove the Router either in index.tsx or in Routes.tsx
According to you file naming I would remove the Router in Routes.tsx
It seems like you are using the history package for navigation. If you are using the react-router v5, then you may have to downgrade the history package to 4.10.1 until the history package developers issue a new release with the fix. As of writing this, the latest release version of history package is v5.0.1, and if you see a new release than this, you can try with that version first to see if it works, before doing the downgrade to 4.10.1
Issue Link -> https://github.com/remix-run/history/issues/804

React Component not rendering on using react router 4

I am working on a simple demo React project. I have a Home component which gets rendered when directly placed in main.js but when placed inside the Router in Routes.js, it is not rendered. Can anyone let me know what am I doing wrong here?
main.js file
import React from "react";
import {render} from "react-dom";
import {App} from "./app/App";
import Routes from "./app/Routes";
render( <Routes />,
document.getElementById("root")
)
Routes.js file
import React from "react";
import {
BrowserRouter as Router,
Route,
Switch,
} from "react-router-dom";
import {App}
from "./App";
import Home from "./components/Home";
export default function Routes(props) {
console.log('Routes');
return (
<Router>
<App>
<Switch>
<Route path="/" exact component={Home} />
</Switch>
</App>
</Router>
)
}
App.js file
import React from "react";
import Header from "./components/Header";
export class App extends React.Component {
render() {
console.log("App render");
return (
<div>
<h1> Welcome to React </h1>
<Header/>
</div>
)
}
}
Header.js file
import React, {Component} from 'react';
import {NavLink} from 'react-router-dom';
export default class Header extends Component {
render() {
console.log("Header render");
return (
<div>
<NavLink to="/" exact>
Home
</NavLink>
</div>
)
}
}
Home.js file
import React, {Component} from "react";
export default class Home extends Component {
render() {
console.log("Home render");
return (
<div>
<h2>Hello World!</h2>
</div>
)
}
}
This is because you are using App component as the wrapper of whole app, and defined the Switch as the children of App component, so you need to use this.props.children inside App.
Like this:
export class App extends React.Component {
render() {
console.log("App render");
return (
<div>
<h1> Welcome to React </h1>
<Header/>
{this.props.children}
</div>
)
}
}
Consider this example to make the whole picture more clear, if you write:
<App>
<Home />
</App>
Means Home will get passed as children to App component, automatically it will not get rendered inside App, you need to put this.props.children somewhere inside App.
With react-router-v4 which you seem to be using, it is possible to have dynamic Routing which means you can add the Routes within nested components and hence apart from the solution that #MayankShukla suggested you could also keep the <Switch> and other routes within App like
export default function Routes(props) {
console.log('Routes');
return (
<Router>
<App/>
</Router>
)
}
export class App extends React.Component {
render() {
console.log("App render");
return (
<div>
<h1> Welcome to React </h1>
<Header/>
<Switch>
<Route path="/" exact component={Home} />
</Switch>
</div>
)
}
}
You could read more about the advantages of Dynamic Routing here

react router 4 nested components not working properly

I am switching from react-router 3.x to 4.x and I am not able to render nested routes.
I bootstrapped an application using create-react-app
index.js file
import React from 'react';
import ReactDOM from 'react-dom';
import Routes from './routes';
import './index.css';
ReactDOM.render(<Routes />, document.getElementById('root'));
routes.js file
import React from 'react';
import _ from 'lodash';
import {
BrowserRouter as Router,
Route,
} from 'react-router-dom';
import { dojoRequire } from 'esri-loader';
import EsriLoader from 'esri-loader-react';
import App from './components/App';
import Home from './components/Home';
/**
* Helper component to wrap app
*/
class AppWrapper extends React.Component {
/**
* Util function to render the children
* Whenever a state change happens in react application, react will render the component again
* and we wish to pass the updated state to the children as props
*/
renderChildren() {
const {children} = this.props;
if (!children) {
return;
}
return React.Children.map(children, c => React.cloneElement(c, _.omit(this.props, 'children'), { }));
}
render() {
const child = this.renderChildren();
return (
<App {...this.props}>
{child}
</App>
);
}
}
/**
* Root Loader component to load esri api
*/
class LoaderComponent extends React.Component {
constructor(props) {
super(props);
this.state = { loaded: false };
}
/**
* Callback fired when arc GIS api is loaded
* Now load the requirejs modules using dojorequire
*/
esriReady() {
dojoRequire(['esri/Map', 'esri/views/MapView'], (Map, MapView) => {
this.setState({ Map, MapView, loaded: true });
});
}
render() {
const options = {
url: 'https://js.arcgis.com/4.3/',
};
return (
<div>
<EsriLoader options={options} ready={this.esriReady.bind(this)} />
<AppWrapper {...this.state}>
<Route exact path="/home" component={Home} />
</AppWrapper>
</div>
);
}
};
const Routes = (props) => (
<Router {...props}>
<Route exact path="/" component={LoaderComponent} />
</Router>
);
export default Routes;
App and home components are simple div tags that renders <div>Hello world App</div> and <div>Hello world Home</div>
The App component renders perfectly, but when I navigate to http://localhost:3000/home component I see an empty page.
What I would like to do is
When the user launched the app the user should be redirected to /home and I would like to define two additional routes for App Component
<Route exact path="/a" component={A} />
<Route exact path="/b" component={B} />
Currently I am not able to redirect to /home on app load and not able to define nested routes for App Component.
NOTE: This above code was working fine for react-router version 3.x. To redirect on page load I would use IndexRedirect.
I already had a look at this and this question and I tried all possible solutions in those questions but none is working.
I would like to have all the route handling in routes.js file.
You could achieve such routing with Switch and Redirect:
import React from 'react';
import {Route, Switch, Redirect} from 'react-router-dom';
import {LoaderComponent, AppWrapper , Home, A, B} from './mycomponents';
const routes = (
<LoaderComponent>
<AppWrapper>
<Switch>
<Route exact path='/' render={() => <Redirect to='/home' />} />
<Route exact path='/home' component={Home} />
<Route exact path='/a' component={A} />
<Route exact path='/b' component={B} />
</Switch>
</AppWrapper>
</LoaderComponent>
);
export default routes;
And use the routes.js file something like this:
import React from 'react';
import {render} from 'react-dom';
import {Router} from 'react-router-dom';
import createHistory from 'history/createBrowserHistory';
import routes from './routes';
const history = createHistory();
const App = () =>
<Router history={history}>
{routes}
</Router>;
render(<App />, document.getElementById('root'));
Hey I have implemented react-router-v4, for a simple sample project. here is the github link : How to Implement the react-router-v4. please go through it. very simple.
to run : clone it and hit npm install. hope this will help to you.

Categories