React Router renders two components instead of one [duplicate] - reactjs

This question already has answers here:
React Router v4 renders multiple routes
(3 answers)
Closed 5 years ago.
I am trying to render two different components with the path /acts and /acts/lite. The problem is that when I go to /acts/lite I get both components rendered instead of just the one that correspond to this path...
For example, when I'm navigating to /acts I get this:
And when I'm navigating to /acts/lite the text Test Acts is still there...
This is my Router:
<Router>
<div>
<App>
<Route exact path="/login" component={LoginPage}/>
<div className="flexible-width container">
<Route path="/acts" component={requireAuth(ActsPage)} />
<Route path="/acts/lite/" component={requireAuth(ActsLitePage)} />
</div>
</App>
</div>
</Router>
ActsPage Component:
class ActsPage extends React.Component {
render() {
return (
<div>
Test Acts
</div>
);
}
}
export default ActsPage;
ActsLitePage Component:
class ActsLitePage extends React.Component {
render() {
return (
<div>
Testing Acts Lite
</div>
);
}
}
export default ActsLitePage;
And this is the Component that shows the sidebar with those two links:
var Sidebar = React.createClass({
render() {
var sidebarClass = this.props.isOpen ? 'sidebar open' : 'sidebar';
return (
<div className={sidebarClass}>
<div><Link to="/acts/">Acts</Link></div>
<div><Link to="/acts/lite/">Acts Lite</Link></div>
</div>
);
}
});
export default Sidebar;
I really don't know what I'm doing wrong or why it behaves like this..

You missed "exact" in your route:
<Link exact to="/acts/">Acts</Link>

Related

React JS refresh page every time clicking on menu item using route

I am new to React JS and I am currently building a simple application. I am using Route in order to navigate between components and everything work fine, but if I am on a page and I click again in the menu to navigate to the page, it doesn't refresh its content.
I just want the component to refresh its content every time I click on the item menu.
This is my sidebar class:
class Sidebar extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Router>
<Route render={({ location, history }) => (
<React.Fragment>
<SideNav
onSelect={(selected) => {
const to = '/' + selected;
if (location.pathname !== to) {
history.push(to);
}
}}>
<SideNav.Toggle />
<SideNav.Nav>
<NavItem eventKey="Cars">
<NavIcon>
Cars
</NavIcon>
</NavItem>
<NavItem eventKey="Bicycles">
<NavIcon>
Bicycles
</NavIcon>
</NavItem>
</SideNav.Nav>
</SideNav>
<main>
<Switch>
<Route exact path="/" component={props => <Home />} />
<Route
exact path="/Cars"
render={() => !isAllowed ?
<Home /> :
<Cars/>
} />
<Route
exact path="/Bicycles"
render={() => !isAllowed ?
<Home /> :
<Bicycles />
} />
</Switch>
</main>
</React.Fragment>
)}
/>
</Router>
)
}
}
This is my Cars Component class:
import React, { Component } from 'react';
class Cars extends Component {
render() {
return (
<div style={{ textAlign: 'center', marginLeft: '295px' }} >
<form>
<h1>Hello</h1>
<p>Enter your car name:</p>
<input
type="text"
/>
</form>
</div>
)
}
}
export default Cars;
For ex. if I text something in input and after that I click on the item menu, I want that input to be refreshed.
In order to "refresh" (or in React world called Re-render) the content of the component you need to change it's state, and that is how React works. As I can see you don't have any state in your component so if you can specify what you wanna "refresh" we can help you.
The heart of every React component is its “state”, an object that determines how that component renders & behaves. In other words, “state” is what allows you to create components that are dynamic and interactive.
Quick example from somewhere on the internet :
import React from 'react';
class Person extends React.Component{
constructor(props) {
super(props);
this.state = {
age:0
this.incrementAge = this.incrementAge.bind(this)
}
incrementAge(){
this.setState({
age:this.state.age + 1;
});
}
render(){
return(
<div>
<label>My age is: {this.state.age}</label>
<button onClick={this.incrementAge}>Grow me older !!<button>
</div>
);
}
}
export default Person;
The age of inside of the label is being Re-rendered (or "refresh") every time when the user clicks on it since its state is changing.
Here is an official documentation and I would recommend you read it, it will clarify a lot of issues you are facing.
https://reactjs.org/docs/state-and-lifecycle.html

How to set a state variable in the parent of a React Route?

I am having fun implementing routing in my app, but find it hard to set a page title in the parent with the name of the screen that Route is rendering, like this:
class App...
setTitle(title) {
this.state.screentitle = title
this.setState(this.state)
}
...
<h1>{this.state.title}</h1}
...
<Route path='/Search' render={props => <ScreenSearch setTitle={this.setTitle} {...props} />} />
and each child holds the actual title text, like:
class ScreenSearch...
componentDidMount() {
this.props.setTitle("Search");
}
While this actually works, I would prefer to keep the title texts for all child screens in the parent, together with all Route rules. After all, the child objects should do just their job, like implementing a search page, but have no need to know what it is called at the parent level.
Also, this seems a complex way with too much code to just set a stupid title.
As a beginner with React Route, I would like to ask if there is a better way.
You can implement a Layout component that will have shared items on it like page title,navigations etc.
Layout Component
class DefaultLayout extends React.Component{
render() {
return (
<div>
<div className="header">
<h1>Company Name</h1>
<Navigation />
</dv>
<div className="content">
<h1>{this.props.title}</h1>
{this.props.children}
</div>
<div className="footer">
<p>Copyright (c) 2014 Company Name</p>
</div>
</div>
);
}
};
module.exports = DefaultLayout;
and the child page
var React = require('react');
var DefaultLayout = require('./DefaultLayout');
class PageOne extends React.Component{
this.state={
title:"Page 1"
}
render() {
return (
<DefaultLayout title={this.props.title}>
<p>The page's content...</p>
<p>goes here.</p>
</DefaultLayout>
);
}
}
This is a way to implement it, if you have many routes you can create your own Route component where you give the title as a prop
<Route path='/Search' render={props => {
this.state.screentitle!==this.setTitle && this.setTitle(this.setTitle)
return <ScreenSearch {...props} />}
/>
After seeing your comment I understand you want to remove the set Title so you will need the location from react-router and an object something like this:
let routeTitles = {
/'search': 'The search' ,
'/jobs': 'Look for a job'
}
<Title>{routeTitles[this.props.location.pathname]}</Title>

Adding a Link as a child of a Router with ReactDOM.render yields "You should not use <Link> outside a <Router>"

I am looking for a way to use ReactDOM.render to create a Link within a react router. The setup more or less looks like this:
const router = (
<div>
<Router>
<Route path="/map" component={Map}/>
</Router>
</div>
);
The relevant parts of Map.jsx look like this:
const MapPopup = () => {
return (
<Link to={`/map/add`} />
)
}
class Map extends React.Component {
componentDidMount() {
this.map = L.map('map')
//...stuff...
this.map.on('contextmenu', event => {
popup
.setLatLng(event.latlng)
.addTo(this.map)
.setContent(
ReactDOM.render(
MapPopup(),
document.querySelector('.leaflet-popup-content')
)[0]
)
.openOn(this.map)
})
}
render() {
return (
<React.Fragment>
<div id="map" />
</React.Fragment>
)
}
}
I am basically trying to add a Link to the map popup provided by leaflet (I can't use react-leaflet for this project). If I however return the MapPopup directly in the render function it works (obviously not in the popup but the Link does work this way).
<React.Fragment>
<div id="map" />
<MapPopup />
</React.Fragment>
Does anyone have an idea how I can tackle this rather unusual problem?
I am using "react-router-dom": "4.3.1".
This is the expected error since <Link> component expects ancestor component to be of router type (<BrowserRouter>, <MemoryRouter>, <Router> ... ), refer this thread for a more details.
For your scenario to circumvent this limitation ReactDOM.createPortal could be utilized instead of ReactDOM.render:
<Route
path="/popup"
render={() => (
<Popup>
<div>
Some content goes here
<Link to="/map"> Back to map</Link>
</div>
</Popup>
)}
/>
where
class Popup extends React.Component {
render() {
return ReactDOM.createPortal(
this.props.children,
document.querySelector("#link-render-div")
);
}
}
and
Here is a demo for your reference

ReactJS, React-Router: Calling parent function

I am working on a React App, trying to call a parent method from a child component, some code of the parent component below:
class NavigationBar extends Component {
constructor(props){
super(props);
this.state={isLoggedIn: false};
}
updateLoginState(){
alert("Login from NavigationBar");
}
GetBar() {
//const isLoggedIn = this.props.isLoggedIn;
if (false){ //isLoggedIn
return this.UserNavBar();
}
return this.StrangerNavBar();
}
StrangerNavBar(){
return (
<div>
<HashRouter>
<div>
{/* ... */}
<div className="content">
<Route exact path="/LoginCC" loginUpdate={this.updateLoginState} component={LoginCC} />
</div>
</div>
</HashRouter>
</div>
);
}
render() {
return (
this.GetBar()
);
}
}
export default NavigationBar;
This component is supposed to redirect the user to different content pages based on whether or not he is logged in, using a Router. If a button is clicked in LoginCC.js the method updateLoginState should be invoked which just displays a message for now. The child content page LoginCC.js looks as follows:
class LoginCC extends Component {
constructor(props){
super(props);
this.state = {isLoggedIn: false};
}
render() {
return (
<div>
<HashRouter>
{/* ... */}
<Button variant="primary" size="lg" block onClick={this.props.loginUpdate}>
Log in
</Button>
{/* ... */}
</HashRouter>
</div>
);
}
}
export default LoginCC;
I passed the method reference as a prop to LoginCC when rendering this component using the Router, so a message should pop up if I press the button, but nothing happens.
Am I passing the prop incorrectly or something else I've missed? I'm new to React so any help is appreciated.
Route doesn't pass any custom props to components. You should use other method to pass functions.
One of solutions is:
<Route exact path="/LoginCC" render={
props => <LoginCC {...props} loginUpdate={this.updateLoginState}/>
} />
Note that updateLoginState will not get this when called. You should either bind it or declare it as an arrow function to get the correct this.
Also check the Context documentation.

React this.props.children is undefined

I have the following router config:
ReactDOM.render((
<Router history={History.createHistory()}>
<Route path="/" component={Page}>
<IndexRoute component={OverviewDashboard}/>
<Route path="/:env" component={Env}>
<IndexRoute component={EnvOverview}/>
</Route>
</Route>
</Router>
), document.getElementById('page'));
and the following component definitions:
const Page = React.createClass({
render: function () {
return (
<main>
<Header />
{this.props.children}
</main>
);
}
});
const OverviewDashboard = React.createClass({
render: function () {
return (
<section>
<Env name="Env1" />
<Env name="Env2" />
</section>
);
}
});
const Env = React.createClass({
render: function() {
return (
<section className="env">
<header>{this.props.params && this.props.params.env || this.props.name}</header>
{this.props.children}
</section>
);
}
});
const EnvOverview = React.createClass({
render: function() {
return (
<div>
<Jobs env={this.props.params.env}/>
<Runtime env={this.props.params.env}/>
</div>
)
}
});
I've ommitted the definitions of <Header>, <Jobs> and <Runtime> because they are not relevant.
When i go to / link {this.props.children} within <Env> component is undefined and thus not rendered.
However when I go to "/Env1" {this.props.children} is set to <EnvOverview> correctly and displayed.
I am new to React and probably I am using the routing component incorrectly. Could someone explain how do I render <EnvOverivew> also when accessing / link ?
When you go to /Env, react-router renders your /:env route, whose component is Env. Inside this route, you have an IndexRoute. This IndexRoute renders the EnvOverview component as its parent route children. Thus, you get {this.props.children} on this case.
When you enter the / route, it triggers your IndexRoute inside App, whose component is OverviewDashboard. It doesn't enter the /:env route because you didn't provide a parameter to match :env.
Inside your OverviewDashboard, you are manually rendering two Envs, without providing their children (check this). Thus, inside these two Envs, {this.props.children} equals undefined.
I don't know what your requirements are, but you could change the OverviewDashboard render method to this:
render: function () {
return (
<section>
<Env name="Env1"><EnvOverview name="Env1" /></Env>
<Env name="Env2"><EnvOverview name="Env2" /></Env>
</section>
);
}
And inside EndOverview render method, change env={this.props.params.env} to env={this.props.params.env || this.props.name}.
I don't know if it's the best solution to your case, but I hope why {this.props.children} was undefined is clarified ;)

Resources