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
Related
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.
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
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
I have this index.js:
<Provider store={store}>
<Router history={history}>
<App/>
</Router>
</Provider>
this App.js:
<Switch>
<Route exact path="/" component={Home} />
<Route
path="/login"
render={() => <Login userError={this.state.userError} />}
/>
<Route path="/registration" component={Registration} />;
</Switch>
and Home.js:
<div className="Home">
<Header/>
<div className="content">
<Sidenav/>
<Switch>
<Route path="/friends" component={Friends}/>
</Switch>
<Feed/>
</div>
</div>
I want Friends component to be rendered inside content block, but now if I try to reach /friends route via Link I am getting blank page. If I set /friends Route in App.js, it will be OK, but I won't have it in my content class, because it will be another page.
May you give me a hand with that?
Also in feature I will be have more items to display in content, that's why I put Switch in Home.js
Thanks in advance!
Move your content class and <Friends>
The issue you're having is that the component Home is not rendering when you visit /friends because it will only render when you go to /
To fix this just move the Route into the App.js file, along with the content class into the Friends component.
To make this easier, you could make your content class into a component. This way you could wrap it around all of the stuff you render.
Or move <Friends> and wrap content
What I mean by this is that you could also create your own Route component that wraps whatever component passed to it in a Content component. It might look similar to this:
const ContentRoute = ({ component, ...props }) => (
<Route {...props} component={() => (
<Content>
<component />
</Content>
)}>
</Route>
)
You can access demo here
Here what I have done. This demonstrates how to set layout when page's changing.
- src/
-- components/
--- Header.js
--- Sidenav.js
-- pages/
--- Home.js
--- Login.js
--- withBase.js
-- App.js
-- BaseLayout.js
-- routes.js
At first, let's make dummy components.
components/Header
import React from 'react';
export default () => (
<div>
This is Header.
</div>
);
components/Sidenav
import React from 'react';
export default () => (
<div>
This is Sidenav.
</div>
);
Then, pages.
pages/Home
import React from 'react';
import { NavLink } from 'react-router-dom';
import withBase from './withBase';
const Home = () => (
<div>
<p>Welcome Home!!!</p>
<NavLink to="/login">Go to login page</NavLink>
</div>
);
export default withBase(Home);
pages/Login
import React from 'react';
import { NavLink } from 'react-router-dom';
import withBase from './withBase';
const Login = () => (
<div>
<p>You have to login here...</p>
<NavLink to="/">Go home</NavLink>
</div>
);
export default withBase(Login);
pages/withBase
import React from 'react';
export default WrappedComponent => (
class extends React.Component {
componentDidMount() {
this.props.showHeaderSidenav();
}
render() {
return <WrappedComponent />;
}
}
);
As you see, withBase is a HOC. It runs showHeaderSidenav when the page is mounted.
App
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import BaseLayout from './BaseLayout';
import routes from './routes';
export default class extends React.Component {
state = {
withHeaderSidenav: true
}
showHeaderSidenav = (withHeaderSidenav = true) => {
this.setState({ withHeaderSidenav });
}
render() {
return (
<BaseLayout withHeaderSidenav={this.state.withHeaderSidenav}>
<Switch>
{routes.map(route => (
<Route
exact
key={route.path}
path={route.path}
render={() => (
<route.component
showHeaderSidenav={() => this.showHeaderSidenav(route.withHeaderSidenav)}
/>
)}
/>
))}
</Switch>
</BaseLayout>
);
}
}
BaseLayout
import React from 'react';
import Header from './components/Header';
import Sidenav from './components/Sidenav';
export default ({ withHeaderSidenav, children }) => (
<div>
{withHeaderSidenav && <Header />}
<div className="content">
{withHeaderSidenav && <Sidenav />}
{children}
</div>
</div>
);
We can say that BaseLayout is like a wrapper. It contains dynamic components which will be shown based on withHeaderSidenav prop.
Finally...
routes
import Home from './pages/Home';
import Login from './pages/Login';
export default [
{
path: '/',
component: Home,
withHeaderSidenav: true
},
{
path: '/login',
component: Login,
withHeaderSidenav: false
},
];
You could have moved(declared) content component inside Friends component. I do not see the reason why content component should live outside of Friends component. You can declare content component inside any component that needs it.Content component does not have to mess with routing implementation
I am a noob just started learning React a few days ago. I have a problem while I was working with react router v4.
I have a home page component, like this:
import React, {Component} from 'react';
class AboutPage extends Component {
render() {
return (
<div>
<h1>About</h1>
<p>this is the about page</p>
</div>
);
}
}
export default AboutPage;
and here is the App.js:
import React, { Component } from 'react';
import './App.css';
import {
Link, Route, Switch,BrowserRouter
} from 'react-router-dom'
import Header from './components/common/Header';
import HomePage from './components/home/HomePage';
import AboutPage from './components/about/AboutPage';
class App extends Component {
render() {
const home = () =><h1> Home </h1>
const about = () =><h1> about </h1>
return (
<BrowserRouter>
<div className="container-fluid">
<Header/>
<Switch>
<Route exact path="/" components={home}/>
<Route path="/about" components={about}/>
</Switch>
</div>
</BrowserRouter>
);
}
}
export default App;
the headers file is just a nav element I want it for all the component.
The problem is if I click either homepage or about page, it doesn't display the content of these components. It just shows the nav element and that's all.
I built this app using creat-react-app. Is there anything wrong in my code? Thx.
Reading the docs looks like that the Route component is expecting the property component and not components. I also believe that what you want to render is your HomePage and AboutPage component