Prevent header menu to re render when navigating through page - reactjs - reactjs

I would like to ask if its possible not to re render the header component when navigating through page, because when I do console.log() in the header component it always fire up when navigating through page. Here's is my code:
// Route.jsx
<Route component={HeroesPage}>
<Route path="/reactjs" component={HeroesComponent}></Route>
<Route path="/reactjs2" component={HeroesCreateComponent}></Route>
</Route>
// HeroesPage.jsx
import React from 'react';
import Header from "components/common/Header.jsx";
class HeroesPage extends React.Component {
render() {
return (
<div>
<Header />
{ this.props.children }
</div>
);
}
}
export default HeroesPage;
// Header.jsx
import React from "react";
import { Link } from 'react-router';
class Header extends React.Component {
componentDidMount(){
console.log('header loaded');
// If I will add an api call here, it will fetch to the server everytime I navigate to the page
}
render(){
return(
<ul className="nav nav-tabs">
<li><Link to="/reactjs">Reactjs 1</Link></li>
<li><Link to="/reactjs2">Reactjs 2</Link></li>
</ul>
);
}
}
export default Header;

Try refactoring routes like
<Route path="/" component={HeroesPage}>
<Route path="reactjs" component={HeroesComponent} />
<Route path="reactjs2" component={HeroesCreateComponent} />
</Route>

Related

BrowserRouter does not render view of another component in the same page

I am having a very simple app with Carousel. Each slide has a button. When the user clicks on the buttons, it should show the corresponding page at the bottom container. I am passing individual Slide from the parent component as a property called content. Below is my code
Snippet from Slidebar.js which is passing the slides to SlideContent
<div css={SliderBarCss}>
{this.props.slides.map((slide, i) => (
<SlideContent key={i} content={slide} />
))}
</div>
SlideContent.js
/** #jsx jsx */
import React from 'react';
import { BrowserRouter, Link } from 'react-router-dom';
import Routes from './Routes';
import { css, jsx } from '#emotion/core'
export default class SlideContent extends React.Component {
constructor(props) {
super(props);
}
render(){
<div>
<h1>{this.props.content.title}</h1>
<p>{this.props.content.description}</p>
<BrowserRouter>
<Link to={this.props.content.link}><button>{this.props.content.button}</button>
</Link>
</BrowserRouter>
</div>
)
}
}
I have defined the Routes in another file Routes.js
import React, { Component } from 'react';
import { css, jsx } from '#emotion/core'
import { Route, Link, Switch, Redirect } from 'react-router-dom';
import Component1 from '../innerComponents/Component1';
import Component2 from '../innerComponents/Component2';
import HomeComponent from '../innerComponents/Home';
class Routes extends Component{
constructor(props){
super(props);
}
render(){
return(
<Switch>
<Route exact path="/Home" component={HomeComponent} />
<Route exact path="/">
<Redirect to="/Home" />
</Route>
<Route exact path="/Component1" component={Component1} />
<Route exact path="/Component2" component={Component2} />
</Switch>
)
}
}
export default Routes;
below is my app.js where I want my content to show up on button click.
function App() {
return (
<div className="App">
<SliderContainer/>
<BrowserRouter>
<Route component={Routes}></Route>
</BrowserRouter>
</div>
);
}
The URL on the top is changing but the view is not. Really appreciate any help
SOLUTION:
I removed the BrowserRouter element from SlideContent.js and MainContent.js. Added it as a parent to both SliderContainer and MainContent. Shown below are all changed the files
Removed BrowserRouter from SlideContent.js
export default class SlideContent extends React.Component {
constructor(props) {
super(props);
}
render(){
<div>
<h1>{this.props.content.title}</h1>
<p>{this.props.content.description}</p>
<Link to={this.props.content.link}><button>{this.props.content.button}</button>
</Link>
</div>
)
}
Removed BrowserRounter from MainContent.js
class MainContent extends React.Component{
render(){
return(
<Route component={Routes}></Route>
)
}
}
Added BrowserRouter to the parent of SliderContainer and MainContent
app.js
function App() {
return (
<div className="App">
<BrowserRouter>
<SliderContainer/>
<MainContent/>
</BrowserRouter>
</div>
);
}
export default App;

React Router Switch did not work properly

I am new to react and trying to create simple navigation which has two menu items (Dashboard and Users). But when I click on Users link it did not render that page content, but dashboard content getting hide. Someone please help me to resolve the issue.
App.js
import React, { Component } from 'react';
import Login from './pages/Login';
import { BrowserRouter as Router, Switch, Route, Link, Redirect, withRouter } from 'react-router-dom';
import { history } from './_helpers/history';
import { authenticationService } from './_services/authentication.service';
import Users from './pages/users/Users';
import Dashboard from './pages/Dashboard';
class App extends Component {
constructor(props) {
super(props);
this.state = {
currentUser: null
};
}
componentDidMount() {
authenticationService.currentUser.subscribe(x => this.setState({ currentUser: x }));
}
logout() {
authenticationService.logout();
history.push('/login');
}
render () {
const { currentUser } = this.state;
return (
currentUser ? (
<Router>
<div id="wrapper">
<ul>
<li><Link to={'/'} className="nav-link" > <i className="fas fa-fw fa-tachometer-alt"></i> <span>Dashboard</span> </Link></li>
<li><Link to={'/users'} className="nav-link" > <i className="fas fa-fw fa-users"></i> <span>Users</span> </Link></li>
</ul>
<Switch>
<Route path='/' component={Dashboard} />
<Route path='/users' component={Users} />
</Switch>
</div>
</Router>
) : <Login />
);
}
}
export default App;
Dashboard.js
import React, { Component } from 'react';
import { Formik, Field, Form, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { authenticationService } from '../_services/authentication.service';
import { history } from '../_helpers/history';
class Dashboard extends Component {
constructor (props){
super(props);
if (authenticationService.currentUserValue) {
history.push('/');
}
this.state = {
isPage: '/'
}
}
render (){
if(this.state.isPage == window.location.pathname){
return (
<div className="container">
dashboard
</div>
)
}else{
return '';
}
}
}
export default Dashboard;
Users.js
import React, { Component } from 'react';
import { Formik, Field, Form, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { authenticationService } from '../../_services/authentication.service';
import { history } from '../../_helpers/history';
class Users extends Component {
constructor (props){
super(props);
if (authenticationService.currentUserValue) {
history.push('/');
}
this.state = {
isPage: '/users'
}
}
render (){
if(this.state.isPage == window.location.pathname){
return (
<div className="container">
users
</div>
)
}else{
return '';
}
}
}
export default Users;
In App.js component; make Switch direct child of Router; that will fix the issue. You can refactor your code like so:
<Router>
<Switch>
<div id="wrapper">
<ul>
<li><Link to={'/'} className="nav-link" > <i className="fas fa-fw fa-tachometer-alt"></i> <span>Dashboard</span> </Link></li>
<li><Link to={'/users'} className="nav-link" > <i className="fas fa-fw fa-users"></i> <span>Users</span> </Link></li>
</ul>
<Route path='/' component={Dashboard} />
<Route path='/users' component={Users} />
</div>
</Switch>
</Router>
but dashboard content getting hide.
Can you elaborate on that? I'm not quite understanding what you mean.
The problem may lie with your use of react lifecycles.
authenticationService.currentUser.subscribe()
is set on componentDidMount() so only after the JSX gets mounted to the DOM. Your Users component is checking authenticationService.currentUserValue on the constructor which runs first before it gets mounted. authenticationService.currentUserValue maybe giving you a falsy which will kick you out to /. Console log that value or place those inside a componentDidMount so it will only check after the mount.
constructor (props){
super(props);
this.state = {
isPage: '/users'
}
}
componentDidMount() {
if (authenticationService.currentUserValue) {
history.push('/');
}
}
When using the <Switch> component, it will render the first component (in order) that matches the path. Optionally you can put an exact prop on the route so it must match the path 100%.
Your <Dashboard> component is being rendered, however your logic for returning an empty string if the path does not match is preventing you from seeing it. You can move the <Users> route higher, or put an exact prop on your routes.
I've created a small CodeSandbox
https://codesandbox.io/s/festive-worker-t7ly3
I assume your ../../_helpers/history looks like that
import { createBrowserHistory } from "history";
export default createBrowserHistory();
You forget to pass history to Router as props, so other components do not know what is history
<Router history={history}>...</Router>`
According to the documentation, <Switch>
Renders the first child or that matches the location.
In your code you have:
<Route path='/' component={Dashboard} />
<Route path='/users' component={Users} />
The problem is that path='/' actually matches any path, including /users, because /users starts with /. So when the route is /users, the Redirect component renders the Dashboard Route and stops looking for other routes.
To fix this, you could add the exact prop to the / Route. exact means that / will not match anything except paths that are exactly "/":
<Route exact path="/" component={Dashboard} />
<Route path="/users" component={Users} />
Now, if the path is /users, the Dashboard Route no longer matches, and the Switch checks if the next Route matches, which it does!
Fixed & simplified example: https://codesandbox.io/s/lucid-leaf-fkgv9
Note that I have removed some code (like this.state.isPage == window.location.pathname) which seemed to be checking if the route matches. You don't need to worry about this in your components, because React-Router takes care of all the routing for you!
Another solution would be to put the Users Route first so that it is checked first, but this can get messy if you have multiple Routes and want to keep them organized.
Remove this line.
if (authenticationService.currentUserValue) {
history.push('/');
}
this is redirecting you again and again to the same page.

I am trying to go back to the previous page on click of a button in react

import {BrowserRouter as Router, Route} from 'react-router-dom';
import Home from './Home';
class App extends Component {
constructor(props){
super(props);
this.state = {value: true}
this.goBack = this.goBack.bind(this);
}
goBack() {
this.props.history.goBack();
}
render() {
return (
<Router>
<div className="App">
<div className="App-header">
<button onClick={this.goBack}>Go Back</button>
</div>
<Route path="/" exact render={() => <Home value={this.state.value}/>}/>
<Route path="/details/:id" component={DetailView}/>
</div>
</Router>
);
}
}
export default App;
This is code. On click of Back button i want to take me to the previous age. But this goBack() is not working for me. Probably I am making some mistake in using it.I tried couple of ways from guthub and stackover flow but nothing worked.
can you try adding withRouter
import {..., withRouter} from 'react-router-dom';
then change export to
export default wihRouter(App);
App component does not receive history as prop because the Router is rendered inside it, instead you can create a wrapper component that is in the route to use this.props.history.
class App extends Component {
render() {
return (
<Router>
<Route path="/" component={Content} />
</Router>
)
}
}
For the content component:
class Content extends Component {
constructor(props){
super(props);
this.state = {value: true}
this.goBack = this.goBack.bind(this);
}
goBack() {
this.props.history.goBack();
}
render() {
return (
<div className="App">
<div className="App-header">
<button onClick={this.goBack}>Go Back</button>
</div>
<Route path="/" exact render={() => <Home value={this.state.value}/>}/>
<Route path="/details/:id" component={DetailView}/>
</div>
);
}
}
Now, Content component is in the route and can receive the history prop.
Another way you can handle this is to simply render the Content component in App with <Content /> and then use withRouter HOC on Content.
withRouter
PS: You cannot apply withRouter to App component because technically App is outside the Router

Hiding some navigation bar links in some of the react components: React+ React-Router+Typescript

I am a bit new to React.
I have a situation where I need to hide some navigation bar links in some components where as in the rest, I want to display them.
Actually been using react router V4 and typescript.
Need to hide page1 and page2 links when it comes to signup and login pages.
Say I also have a getstarted page that loads when the application is launched , here also I would like to hide the links.
Show the links in rest of the components.
Help would be appreciated
Router Code
import * as React from 'react';
import NavBar from './NavBar';
import SignUp from './SignUp';
import Page1 from './Page1';
import Page2 from './Page2';
import Login from './Login';
import GetStarted from './GetStarted';
import { BrowserRouter as Router , Switch , Route} from 'react-router-dom';
const NotFound = () => (
<div><h1>404.. This page is not found!</h1></div>
);
export default class App extends React.Component<{}, {}> {
render() {
return(
<Router>
<div className='container'>
<NavBar/>
<div className='body'>
<Switch>
<Route exact={true} path='/' component={GetStarted}/>
<Route path='/getstarted' component={GetStarted}/>
<Route path='/signup' component={SignUp}/>
<Route path='/login' component={Login}/>
<Route path='/page1' component={Page1}/>
<Route path='/page2' component={Page2}/>
<Route component={NotFound} />
</Switch>
</div>
</div>
</Router>
)
}
}
Navigation Bar Component
import React from 'react';
import { Link } from 'react-router-dom';
export default class NavBar extends React.Component<{}, {}> {
render() {
return (
<nav className="nav">
/*some logo will be displayed here followed by the links*/
<div className="container">
<ul className="item-wrapper">
<li>
<Link to="/page1">Link 1</Link>
</li>
<li>
<Link to="/page2">Link 2</Link>
</li>
</ul>
</div>
</nav>
);
}
}
Since you provide a login function, surely somewhere in your state you store the information whether a user is logged in or not. Use this state to determine whether to display the links:
import React from 'react';
import { Link } from 'react-router-dom';
export default class NavBar extends React.Component<{}, {}> {
render() {
return (
<nav className="nav">
<div className="container">
{user.loggedIn /* boolean indicating whether user is logged in */ &&
<ul className="item-wrapper">
<li>
<Link to="/page1">Link 1</Link>
</li>
<li>
<Link to="/page2">Link 2</Link>
</li>
</ul>
}
</div>
</nav>
);
}
}

React Router not rendering correctly

I had this working before, but I've made some changes in my app structure which has broken the routes. If I manually go to the route in the browser it works (the correct content loads). The href attribute is not rendering in the html.
I have a warning message in my console:
Warning: Failed prop type: Invalid prop to supplied to Link.
I don't nderstand why it's invalid.
What can I do to improve the code and fix the problem?
Index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import Header from './Header';
import Routes from './Routes';
import '../less/imports.less';
const App = () => (
<div>
<Header />
<main>
<Routes />
</main>
</div>
);
if (typeof window !== 'undefined') {
ReactDOM.render(
(
<App />
), document.getElementById('app'),
);
}
Routes.jsx:
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Dashboard from './Dashboard';
import About from './About';
import Blog from './Blog';
class Routes extends React.Component {
constructor(props) {
super(props);
this.state = {
active: 'active',
};
}
render() {
return (
<Router>
<div>
<nav>
<ul className="block-group">
<li className={this.state.active}><Link to={Dashboard}>Dashboard</Link></li>
<li><Link to={About}>About</Link></li>
<li><Link to={Blog}>Blog</Link></li>
</ul>
</nav>
<Route exact path="/" component={Dashboard} />
<Route path="/about" component={About} />
<Route path="/blog" component={Blog} />
</div>
</Router>
);
}
}
export default Routes;
I have just fixed this by changing the to href to the actual route strings. Not sure how it worked before.
<li className={this.state.active}><Link to="/">Dashboard</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/blog">Blog</Link></li>

Resources