React Router v4 - Cannot read property 'params' of undefined - reactjs

I am a beginner for React. I just need to get the path param from the URL when the page is loaded. My URL is as follows.
http://localhost:3000/explorer/test/111
I have set the route like following,
<Route
exact path="/explorer/test/:id"
component={() => <BlockPage />}
/>
Then in the <BlockPage /> component I need to get the value of :id when the component is loading. Therefore, I added the following code segment to get an id, but it gives me error as in the title.
import React, { Component } from 'react';
import NavigationComp from './Navigation';
import { withRouter } from 'react-router-dom';
const BlockPage = () =>
<div>
<NavigationComp/>
<BlockData />
</div>
class BlockData extends Component {
componentDidMount() {
console.log(this.props.match.params.id);
}
render(){
return(.....)
}
}
export default withRouter(BlockPage);
What is the mistake I did? Can someone explain to me? my objective is to get 111 value from URL into componentDidMount()

when you want to use component you dont need function like you code you can change it like it :
<Route
exact path="/explorer/test/:id"
component={<BlockPage />}
/>
or use render function if you want a function like this
<Route
exact path="/explorer/test/:id"
render={(props) => <BlockPage {...props} />}
/>
then in this.props.match.params you can access your id.

This happens because you the component is sub component of other component which ReactRouter knows. For example,
<BrowserRouter>
<Switch>
<Route path="/Feedback">
<Switch>
<BrowserRouter>
in the above route defnition, the match object in Feedback component is not null, but if we have other component in feedback and tries to access it it's an error.

https://stackoverflow.com/a/57628327/12378703
this works perfectly... try this out
<Route
exact path="/explorer/test/:id"
render={(props) => <BlockPage {...props} />}
/>

This solved my problem:
<CPAroute exact path="/advcampaigns/:id" component={AdvCampaigns}/>
class AdvCampaigns extends React.Component {
constructor(props) {
super(props);
this.state = {
activePage: 1
};
};
componentDidMount() {
console.log(this.props.**computedMatch**.params.id);
}
}

I also faced same problem.
Tried all the above solutions, but didn't work.
Then I just console logged this.props
const productName = this.props.params.<YOUR_DYNAMIC_URL>;
WITHOUT match in this.props
And it worked, don't know how but it worked.
May be helpful for others.

Related

What is the simplest way to pass state while using React Router?

What is the simplest way to pass state while using React Router? My Navi component below is reflecting user being null, as opposed to user being "KungLoad". Thanks.
class App extends Component{
constructor() {
super();
this.state = {user: "KungLoad"};
}
render () {
return(
<div>
<Router>
<Route exact path="/" state component = {Navi} />
</Router>
The simplest way is that you can pass the state as props and then use it in the specified component. For your case, you have to use render instead of component for passing the state as props.
<Route exact path="/" render={() => <Navi user={this.state.user} />} />
This will work but I would recommend to you that the Context API concept of reactJS would be best suited here. You can pass the state or props to all the component using the data provider and all the components will consume the state or props that are being provided by the parent component. . https://reactjs.org/docs/context.html
version 6 react-router-dom
I know the question got answered but I feel this might be helpful example for those who want to use functional components and they are in search of passing data between components using react-router-dom v6.
Let's suppose we have two functional components, first component A, second component B. The component A wants to share data to component B.
usage of hooks: (useLocation,useNavigate)
import {Link, useNavigate} from 'react-router-dom';
function ComponentA(props) {
const navigate = useNavigate();
const toComponentB=()=>{
navigate('/componentB',{state:{id:1,name:'sabaoon'}});
}
return (
<>
<div> <a onClick={()=>{toComponentB()}}>Component B<a/></div>
</>
);
}
export default ComponentA;
Now we will get the data in Component B.
import {useLocation} from 'react-router-dom';
function ComponentB() {
const location = useLocation();
return (
<>
<div>{location.state.name}</div>
</>
)
}
export default ComponentB;
Note: you can use HOC if you are using class components as hooks won't work in class components.
Yiu can pass your state as props to your Navi component like this: <Route exact path="/" render={() => <Navi user={this.state.user} />} />
The other answers are correct, you should pass state down to children components via props. I am adding my answer to highlight one additional way that the Route component can be used. The code looks cleaner and is easier to read if you simply add children to a Route component, rather than use the render or component prop.
class App extends Component {
constructor(props) {
super(props);
this.state = {
user: "KungLoad"
};
}
render() {
return (
<div>
<Router>
<Route exact path="/">
<Navi user={this.state.user} />
</Route>
</Router>
</div>
);
}
}
After making the state and assigning value
this.state = {user: "KungLoad"};
Passing the state value to the router is done like this.
<Router>
<Route exact path="/" render={()=> (<Navi user={this.state.user}/>)} />
</Router>
Or if you want to user is not logged in use a redirect
<Route exact path="/signin" render={()=> (<Redirect to='/signin'/>)}/>

Can't pass props to Route component={{}} but can't access location.pathname in Route render={{}} [duplicate]

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.

React routing - Keeping props within the same view

I currently have a few routing paths in my code which pass in a number of properties to those views. As below:
import React, {Component} from 'react'
import { Route, Switch, Redirect, withRouter } from 'react-router-dom'
import CalendarView from '../calendarView'
import ListView from '../listView'
import AgendaView from '../agendaView'
import propTypes from 'prop-types'
class DiaryRouting extends Component{
render() {
const activities = this.props.activities
return (
<switch>
<Route exact path="/my-diary/" render={() => <Redirect push to="/my-diary/activities/calendar-view/month" component={(props) => <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='activities' activities={activities}/>} /> } />
<Route exact path="/my-diary/activities/" render={() => <Redirect push to="/my-diary/activities/calendar-view/month" component={(props) => <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='activities' activities={activities}/>} /> } />
<Route exact path="/my-diary/jobs/" render={() => <Redirect push to="/my-diary/jobs/calendar-view/month" component={(props) => <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='jobs' activities={activities}/>} /> } />
</switch>
)
}
}
DiaryRouting.propTypes = {
activities: propTypes.array,
}
export default DiaryRouting
I have items being passed in such as selectedViewRange and selectedDiaryType into each route. What I am trying to accomplish to having a variable in this view that holds what route it has gone through and what variable has been passed in. Such as below:
....
state = {
selectedViewRange: null
}
... <Route exact path="/my-diary/activities/"
render={() => <Redirect push to="/my-diary/activities/calendar-view/month"
component={(props) => this.setState(selectedViewRange: 'calendar-view') <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='activities' activities={activities}/>} /> } />
However I keep getting a
Warning: setState(…): Cannot update during an existing state
transition
I have tried using a variable instead of a state and that does not do anything.
Best approach how to tackle this?
First off, I'd strongly suggest to use redux for app state management because setState has never been a reliable and scaleable choice.
Anyways, to answer your question:
For the class component that holds your state for activeRoute (rephased your selectedViewRange for better understanding), you should define a method like:
setActiveRoute = (activeRoute) => { this.setState({ activeRoute }) }
Pass it as a prop to the components that you render in your routes.
So basically, your CalendarView should have an extra prop like setActiveRouteName and you should pass this.setActiveRoute as a callback to setActiveRouteName prop like:
<CalendarView {...props} setActiveRouteName={this.setActiveRoute} ... />
Moving on, in your CalendarView, declare a React lifecycle method componentDidMount. It should look like:
export default class CalendarView extends Component {
componentDidMount(){
this.props.setActiveRouteName('yourRouteName');
}
...other code...
}
This should solve the problem.

React Router v4 prevent from running parent route component

I'm building an article search with React [15.6.1] and Router [4.1.1] and noticed that when trying to access an article directly the previous component is loaded, even thou it's not the one that's being called
// === Layout ==================
class Layout extends React.Component {
render() {
return (
<Provider store={this.props.store}>
<HashRouter>
<div>
<Switch>
<Route exact path="/" component={SearchFilter} />
<Route path="/article/:guid" component={Article} />
</Switch>
</div>
</HashRouter>
</Provider>
);
}
}
// === SearchFilter ==================
class SearchFilter extends React.Component {
componentDidMount() {
console.log('SearchFilter Did Mount');
}
render() {
return (
<div>...</div>
);
}
}
// === Article ==================
class Article extends React.Component {
componentDidMount() {
console.log('Article Did Mount');
}
render() {
return (
<div>...</div>
);
}
}
So when going to the root localhost:3000/#/ it prints
// SearchFilter Did Mount
And when I access an article directly like this localhost:3000/#/article/123456 it prints
// SearchFilter Did Mount
// Article Did Mount
So my question is, how can I prevent it from running the previous route?
Because I would like to dispatch some actions there that would trigger some ajax calls to the webservice.
Thanks
Try this instead :
<HashRouter basename="/">
<div>
<Switch>
<Route exact path="/search" component={SearchFilter} />
<Route path="/article/:guid" component={Article} />
</Switch>
</div>
</HashRouter>
Edit:
For me its working well... So like said... there is something really weird and hidden happening on your machine, or you just put / and then rewrite url to /#/article/123 and it cause the first log stays in the console, but its from the previsous url "/" and if you reload the browser by F5 on the new url /#/article/123 you will see only the "Article did mount"

React Dynamic menu items passed down via router

Another react question here, I have a solution for my problem but to me it doesn't seem very "React" so I was hoping for another solution.
I'm using react router so the bottom of my app.js(entry point) is:
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={Layout}>
<IndexRoute component={Login} ></IndexRoute>
<Route path="searches" component={searches}></Route>
<Route path="notifications" component={notifications}></Route>
</Route>
</Router>
, app);
Now as you can see my overarching component is Layout so, in my mind I want to configure my (reusable) components, for example I want my Layout to pass the title of the menu items to the header component, and then if I'm for example loading a search then I might want to pass functions etc to the search component to hook into it's functionality, so I have the following in layout:
export default class Layout extends React.Component {
constructor() {
super();
}
render() {
const containerStyle = {
paddingRight: '5px'
}
// Figure out which props we want based on location.pathname
const {pathname} = this.props.location;
switch(pathname){
case "/searches":
// So now I need to add some props, functions or anything else to this component
theProps = {someProp: "value"}
const theComponent = React.cloneElement(this.props.children, {theProps})
break;
}
return (
< div style={containerStyle} class="container">
< Header menuItems={this.getMenuItemsForScreen()}/ >
{ theComponent}
< Footer / >
< /div>
);
}
}
So basically in order to pass props from my overarching Layout I have to clone the component and then give it some more props?? It just feels a bit dirty but I can't seem to find a way of embedding this type of logic otherwise?
Thanks
Marc
I think the great thing about these routing components is that they save you from those ugly switches in your components.
I'm not sure which kind of props you want to send to the searches component. In your question is not clear what is the actual problem you are trying to solve instead of using one of the standard approaches in the react-router documentation.
I recommend considering these alternatives:
Refactor your searches component to avoid receiving any props. Try to have each route to have a component that doesn't receive any props. So you move that code that define the props (theProps = {someProp: "value"}) inside the searches component. Or if you need the searches component to be reused with those props and other props in another time, then make a new parent component that defines those props and calls the searches component then. But if those props are to complex and dependent on your app state then maybe you can consider using flux, redux or another state container, and get those from the app state.
If you really need the routing parameters, then make sure the props can be serialized so they can be part of the URL. Check the message route in the code below (copied from RouteConfiguration sample):
import React from 'react'
import { render } from 'react-dom'
import { Router, Route, Link } from 'react-router'
const App = React.createClass({
render() {
return (
<div>
<h1>App</h1>
<ul>
<li><Link to="/about">About</Link></li>
<li><Link to="/inbox">Inbox</Link></li>
</ul>
{this.props.children}
</div>
)
}
})
const About = React.createClass({
render() {
return <h3>About</h3>
}
})
const Inbox = React.createClass({
render() {
return (
<div>
<h2>Inbox</h2>
{this.props.children || "Welcome to your Inbox"}
</div>
)
}
})
const Message = React.createClass({
render() {
return <h3>Message {this.props.params.id}</h3>
}
})
render((
<Router>
<Route path="/" component={App}>
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
<Route path="messages/:id" component={Message} />
</Route>
</Route>
</Router>
), document.body)
In this case your code will have <a href={"/inbox/message/"+id} ...> somewhere in your code and those will provide the props by setting the id parameter in this case.
you use functional component in child component with this code :
<Route path="/:id" component={Child} />
function Child({ match }) {
return (
<div>
<h2>ID:{match.params.id}</h2>
</div>
);
}

Resources