I have this App.jsx that routes to (renders) different components.
But I have set <NavigationBar /> and an h1 tag between Router and Switch because I need to render those two components for every page.
So now what I want is to get the current route name/path name that displays on the browser address bar. This path is changing when I click on different links (Link) to render different components.
But the path value is the same / even though the path is changing for every Link click.
I even used componentDidUpdate but it didn't work as it gave the error
maximum update depth exceeded componentdidupdate
this is my App.jsx
import React, { Component } from "react";
import "./App.css";
import "./css/custom.css";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import NavigationBar from "./pages/homepage-components/1-navbar";
import HomePage from "./pages/HomePage";
import Post from "./pages/Post";
class App extends Component {
constructor(props) {
super(props);
this.state = {
currentPath: "",
};
}
componentDidMount() {
this.setState({
currentPath: window.location.pathname,
});
}
render() {
return (
<Router>
{/* --------------- Navigation Bar --------------- */}
<NavigationBar />
<h1>Path is: {this.state.currentPath}</h1>
{/* --------------- End of Navigation Bar --------------- */}
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/post" component={Post} />
</Switch>
</Router>
);
}
}
export default App;
Even though the different components are rendered as path changes, the value for this.state.currentPath doesn't update.
Can someone help, please?
useLocation hook provides current location:
function NavigationHeader() {
const location = useLocation();
return <h1>Path is: {location.pathname}</h1>;
}
Related
I'm trying to figure out why the component SubPage is not rendering whenever the route path of /sub/:_id is visited (e.g. /sub/5f1c54257ceb10816a13d999). This is the first time I've worked with react routes. The :_id part should presumably accept query parameters from the URL dynamically so I cannot see why this is not working.
I can get the /subs page to fetch the API and render each sub on the page but just not each individual sub page.
The route is as follows near the bottom of App.js: <Route path={"/sub/:_id"} component={SubPage} />
Thanks for any help here. I've made a stackblitz for convenience, or you can see the relevant code below:
And subPage.js:
import React from 'react'
export class SubPage extends React.Component {
render() {
return (
<div className="sub-details-individual">
<h1 class="std-intro">Viewing a Single Subscriber</h1>
<div className="sub-specs">
<div className="sub-specs-inner">
id: {this.props.params._id}
</div>
</div>
</div>
)
}
}
And App.js:
import React, {Component} from "react";
import {navLinks} from "./components/nav-links";
import Root from "./components/Root";
import {
BrowserRouter as Router,
Switch,
Route
} from "react-router-dom";
import {SubPage} from "./components/subPage";
import ShowSubs from "./components/show-subs";
export default class App extends Component {
constructor() {
super();
this.state = {
navLinks: navLinks,
intro: "hello world",
url: "someurl"
}
}
updateURL = (newUrl) => {
this.setState({
url: newUrl
})
}
render() {
return (
<Router>
<Root navLinks={this.state.navLinks} intro={this.state.intro}></Root>
<Switch>
<Route path="/subs">
<p>subs page</p>
{/*this.updateURL('/subs') fails presumably because it causes the rerender infinitely - but how to solve?*/}
<ShowSubs />
</Route>
<Route path="/">
<p>homepage</p>
</Route>
<Route path={"/sub/:_id"} component={SubPage} />
</Switch>
<p>the url: {this.state.url}</p>
</Router>
);
}
}
Two things:
this.props.params._id will crash since you are missing match before params
this.props.match.params._id
few exact props are missing, especially in the subs path:
<Route exact path="/subs">
Note: the exact prop will be useful in the / route as well.
I'm writing my first react application and I wanna use a Switch to show the component corresponding to the route. One of the routes uses a param. The problem is that the match attribute is missing from props so route matching doesn't seem to work (No component is inserted).
When I try to console.log this.props.match it returns undefined.
import { Switch, Route } from 'react-router-dom';
import React, { Component } from 'react';
import Index from './Index';
import Debate from './Debate';
class App extends Component {
componentDidMount() {
console.log(this.props.match); // I get undefined
}
render() {
return (
<div className="App">
<Switch>
<Route path="/debats" component={Index} />
<Route path="/debat/:debateSlug" component={Debate} />
</Switch>
</div>
);
}
}
export default withConfig(App);
I want to be able to access the match attribute so that the correct component is displayed.
Add to this in your Component
import {withRouter} from 'react-router-dom';
export default withRouter(withConfig(App));
I'm using React Router to create a multi page app. My main component is <App/> and it renders all of the routing to to child components. I'm trying to pass props via the route, and based on some research I did, the most common way for child components to tap into props passed down is via the this.props.route object that they inherit. However, this object is undefined for me. On my render() function in the child component, I console.log(this.props) and am return an object that looks like this
{match: Object, location: Object, history: Object, staticContext: undefined}
Doesn't look like the props I expected at all. Here is my code in detail.
Parent Component (I'm trying to pass the word "hi" down as a prop called "test" in all of my child components):
import { BrowserRouter as Router, HashRouter, Route, Switch } from 'react-router-dom';
import Link from 'react-router';
import React from 'react';
import Home from './Home.jsx';
import Nav from './Nav.jsx';
import Progress from './Progress.jsx';
import Test from './Test.jsx';
export default class App extends React.Component {
constructor() {
super();
this._fetchPuzzle = this._fetchPuzzle.bind(this);
}
render() {
return (
<Router>
<div>
<Nav />
<Switch>
<Route path="/" exact test="hi" component={Home} />
<Route path="/progress" test="hi" component={Progress} />
<Route path="/test" test="hi" component={Test} />
<Route render={() => <p>Page not found!</p>} />
</Switch>
</div>
</Router>
);
}
}
Child:
import React from 'react';
const CodeMirror = require('react-codemirror');
import { Link } from 'react-router-dom';
require('codemirror/mode/javascript/javascript')
require('codemirror/mode/xml/xml');
require('codemirror/mode/markdown/markdown');
export default class Home extends React.Component {
constructor(props) {
super(props);
console.log(props)
}
render() {
const options = {
lineNumbers: true,
theme: 'abcdef'
// mode: this.state.mode
};
console.log(this.props)
return (
<div>
<h1>First page bro</h1>
<CodeMirror value='code lol' onChange={()=>'do something'} options={options} />
</div>);
}
}
I'm pretty new to React so my apologies if I'm missing something obvious.
Thanks!
You can pass props to the component by making use of the render prop to the Route and thus inlining your component definition. According to the DOCS:
This allows for convenient inline rendering and wrapping without the
undesired remounting explained above.Instead of having a new React
element created for you using the component prop, you can pass in a
function to be called when the location matches. The render prop
receives all the same route props as the component render prop
So you can pass the prop to component like
<Route path="/" exact render={(props) => (<Home test="hi" {...props}/>)} />
and then you can access it like
this.props.test
in your Home component
P.S. Also make sure that you are passing {...props} so that the
default router props like location, history, match etc are also getting passed on to the Home component
otherwise the only prop that is getting passed down to it is test.
I am building a beginner React app and am not able to understand how to handle my state so that I can redirect to a search results page:
I have a main App component which uses React Router to deliver two components:
1) Landing (/) -- has an input and should take you to /search and show only those objects whose title match your input
2) Search (/search) -- either shows all objects if accessing the page directly or your filtered based upon your input
My question is: if I handle the state in the App component, it will cause the state to update and a rerender upon a user typing in the Landing input element, but how can I get it to go to /search with the updated state? The index route will keep getting hit since it's just a rerender and the user is still on the landing page.
I would like to handle this without redux as this will be a very small app.
Here is the code for my parent component:
import React, { Component } from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { shape, string } from "prop-types";
import Landing from "./Landing";
import Search from "./Search";
import { shows } from "../data.json";
class App extends Component {
constructor(props) {
super(props);
this.state = {
searchTerm: ""
};
this.updateSearchTermHandler = this.updateSearchTermHandler.bind(this);
}
updateSearchTermHandler(searchTerm) {
this.setState({ searchTerm });
}
render() {
return (
<BrowserRouter>
<div className="app">
<Switch>
<Route
exact
path="/"
component={props => (
<Landing
updateSearchTermHandler={this.updateSearchTermHandler}
searchTerm={this.state.searchTerm}
{...props}
/>
)}
/>
<Route
path="/search"
component={props => (
<Search
updateSearchTermHandler={this.updateSearchTermHandler}
shows={shows}
{...props}
/>
)}
/>
</Switch>
</div>
</BrowserRouter>
);
}
}
App.propTypes = {
match: shape({
params: string.isRequired
}).isRequired
};
export default App;
One potential solution is to instead use a <Router> with your own history. You could then call history.replace('/search', { searchTerm: 'foo' })
And then in your Landing component, you will have this.props.history.location.state.searchTerm
See https://reacttraining.com/react-router/web/api/Router for further details on creating history
I'm trying to pass a dynamic state to all the routes in a React router, specifically a shopping cart (an array of objects).
The layout is I have a parent component which contains the router and all the routes, and in that I want to store the cart in state and pass it to the routes (so essentially all routes will have access to it). I've been trying a few different things and troubleshooting it by looking it up on forums for a while but I just can't get it. This is the latest setup I have:
- Main.jsx
// This is the app entry point
import React, { Component } from 'react';
import { render } from 'react-dom';
import RouterHub from './RouterHub.jsx';
render((
<RouterHub />
), document.getElementById('root'));
- RouterHub.jsx
import React, { Component } from 'react';
import { render } from 'react-dom';
import { Router, Route, hashHistory } from 'react-router'
import Home from './Home.jsx';
import Dogs from './Pages/Other.jsx';
class RouterHub extends Component {
constructor(props) {
super(props);
this.addItem = this.addItem.bind(this);
this.state = {
cart: []
};
}
addItem(item) {
let newCart = this.state.cart.splice();
newCart.push(item);
this.setState({cart: newCart});
}
render() {
return(
<Router history={hashHistory}>
<Route path="/" component={Home} cart={this.state.cart} addItem={this.addItem} />
<Route path="/other" component={Other} cart={this.state.cart} addItem={this.addItem}/>
</Router>
);
}
}
export default RouterHub;
- Home.jsx
import React, { Component } from 'react';
import Slideshow from './Home/Slideshow.jsx';
import Navbar from './Constants/Navbar.jsx';
import Footer from './Constants/Footer.jsx';
class Home extends Component {
constructor(props) {
super(props);
}
render() {
return(
<div>
<button onClick={() => this.props.route.addItem('potato')}>click me</button>
<Navbar />
// All the JSX content, I've removed to make it succint
<Footer />
</div>
);
}
}
export default Home;
Essentially what I'm wanting is in Home.jsx, when I click that button, I want another potato added to the cart. However, with this setup I get the error:
bundle.js:46451 Warning: [react-router] You cannot change <Router routes>; it will be ignored
How do I get it so that updating state in the RouterHub passes that to the routes, or is that not possible and I'm doing this all the wrong way?
Thanks for any help
Since you already have a main component for holding your state, you should insert that in the top level Route component something like this:
render((
<Router history={browserHistory}>
<Route path="/" component={RouterHub}>
<Route path="home" component={Home}/>
</Route>
</Router>
), document.getElementById('root'))
Then in your RouterHub component, pass those clone each children components with props, something like this:
{
React.Children.map( this.props.children, (child) => {
return React.cloneElement(child, this.props)
})
}
Bumping into this kind of problems will make you think of using some state management libraries like Redux/Flux.