not getting location react router? - reactjs

id like to change my header color when in my contact page. unable to get location from react router. I get "TypeError: Cannot read property 'pathname' of undefined".
I know I'm missing something simple but i cant seem to figure it out.
import React from 'react';
import './Header.css';
import { NavLink } from 'react-router-dom';
class Header extends React.Component {
render() {
const blackHeader = {"background-color" : 'black'}
const clearHeader = {"background-color" : 'transparent'}
return(
<header style={ this.props.location.pathname === '/Contact' ? { blackHeader } : { clearHeader } }>
<NavLink to={'/'}><h2>Name</h2></NavLink>
<nav>
<ul>
<li><NavLink to={'/'}>Portfolio</NavLink></li>
<li><NavLink to={'Contact'}>Contact</NavLink></li>
<li>Blog</li>
</ul>
</nav>
</header>
)
}
}
export default Header;

You need to pass the location props to your Header component or you can use the HOC withRouter in react-router-dom.
import { NavLink, withRouter } from 'react-router-dom';
export default withRouter(Header)
Edit for clarity:
If you render a component in your router in a Route component:
<Route path='/' component={Home} />
This will give that component (Home in this case) access to the location props which can then be passed to children of that component.
import React, { Component } from 'react'
import OtherComponent from './OtherComponent'
class Home extends Component {
render() {
return (
<div>
<OtherComponent locations={this.props.locations}
</div>
)
}
}
export default Home;
However, if the component is not passed through Route it will not have access to location props. For example, if you put your header outside of your routes, something like:
<Router>
<Header/>
<Switch>
<Route path='/' component={Home}/>
</Switch>
</Router>
It will not have location props.
I'm not sure how your component tree is set up but, I hope this gives a little clarity to how the location props are accessed and used in your app.
The documentation is a good place to get some more clarity on the workings of react router as well https://reacttraining.com/react-router/core/api/Route/route-props

Yea, I can see in the contact path
<li><NavLink to={'Contact'}>Contact</NavLink></li> ,
it should be something like
<li><NavLink to='/Contact'>Contact</NavLink></li>.
You have to include the back slash, I mean this ' / ' before the pathname. So try it now.

Related

props are not accessible in the function component

Inside user.js file the props are coming as empty I am unable to access the match in props. I don't understand why props is coming as empty object.
App.js file
import "./App.css"
import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom'
import Home from "./components/Home";
import About from "./components/About";
import User from "./components/User";
function App() {
return (
<div className="container">
<Router>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/user/john">User</Link>
</li>
</ul>
<div>
<Routes>
<Route exact path="/" element={<Home/>}></Route>
<Route exact path="/about" element={<About/>} ></Route>
<Route exact path="/user/:name" element={<User/>}></Route>
</Routes>
</div>
</Router>
</div>
);
}
export default App;
user.js file
import React from 'react'
function User({match}){
return (
<div>
User - {match.params.name}
</div>
)
}
export default User
Inside user.js file the props are coming as empty I am unable to access the match object in props. I don't understand why props is coming as empty object.
The User component isn't passed any props:
element={<User/>} // <-- no props are passed to User
With the way routed components are rendered in react-router-dom v6.x now they no longer are passed props (history, location, and match, or any others) and these must now be accessed via their respective hooks.
history object is now a navigate function and accessed via useNavigate
location object accessed via useLocation
match object accessed via useMatch
However, the route params accessed directly via the useParms hook!
For a give route:
<Route exact path="/user/:name" element={<User/>} />
User can use the useParams hook to access the name route path parameter:
import React from 'react';
import { useParams } from 'react-router-dom';
function User() {
const { name } = useParams();
return (
<div>
User - {name}
</div>
)
}
You have to use the hook useMatch hook to access the current match.

React Router <Link> and <Route> not passing state to Component

React Router: Link and Route not passing state to Component
I’m building an online shopping app with React, Redux, and React Router. On the home page, all items are displayed. I want users to be able to click on an image and be taken to a page with item details for that item.
Here's how I’m trying to do that:
In App.js, I’m using BrowserRouter, Route, and Switch for routing
In Home.js, I’m using react-router's Link to link an image on the Home page to an item details page; the Link is supposed to pass the ID to the Item component as state (see the Home.js code below)
Item.js is supposed to render an items detail page. The ID of the item will be passed to the Item component, which will render the details for that item.
This seems simple, and I’ve seen a lot of discussion about this on SO and across the web. Here are some that I have already tried:
Display item details after clicking Link in React Router
React router how to click thru to detail components
Passing values through React-Router v4 <Link />
Tyler McGinnis - Pass props to React Router's Link component
Medium - How to pass props to React routes components
I’m able to get the URL to change to correspond to the item’s ID, but I can’t get the Item component to render with the right information, and sometimes I get errors depending on the method I’m using. I hard coded the words "Item Component" inside a div in Item.js, and that will render when I click on the images from the Home page. Otherwise, nothing will render or I receive the two following TypeErrors:
App.js:
<Route path="/products" render={(props) => (<Item {...props} id={this.props.location.id} />) }/>
export default withRouter(App);
<App /> is wrapped in <BrowserRouter> in index.js.
I receive the following error when clicking on an image from the home page:
TypeError: Cannot read property 'props' of undefined
When the above “render” code is removed from and replaced with “component={ Item }”, I receive this error from Item.js:
TypeError: Cannot read property 'location' of undefined
Here are my other code snippets:
Item.js:
class Item extends React.Component {
constructor() {
super();
this.state = {
item: this.props.location.state
}
}
render() {
let item = this.state.item;
return (
<div className="item" key={item.id}>
<div className="item-image">
<img src={item.img} alt={item.title} />
</div>
<div className="card-component">
<span className="item-title">{item.title}</span>
<p className="item-price"><b>${item.price}</b></p>
<p className="item-desc">{item.desc}</p>
</div>
</div>
);
}
}
Home.js (where all items are displayed), the item details page is linked like so:
<Link to =
{{ pathname: `/products/${item.category}`,
search: `?id=${item.id}`,
state: {id: `${item.id}`} }}
component={ Item }>
<img src={item.img} alt={item.title} />
</Link>
UPDATE
I have wrapped the <Item/> component in withRouter().
In Item.js, if I set the state in the constructor as { item: this.props.location.state }, nothing renders as it should (only a dollar sign is on the screen, since the price section contains a hard coded dollar sign).
I tried setting the Item.js state to null in the constructor, then calling this.setState with { item: this.props.location.state } in componentDidMount(). When I do this, I receive the following error:
TypeError: Cannot read property 'img' of null
Also, if I leave the render={(props) => (<Item {...props} id={this.props.location.id} />) } in the <Route> in App.js, I receive this error:
TypeError: Cannot read property 'props' of undefined
If I change <Route> back to component={ Item }, and the Item.js constructor state is set to { item: this.props.location.state }, just the dollar sign renders.
UPDATE: include App.js and Routes.js code
App.js
import React from 'react';
import {
BrowserRouter as Router,
withRouter,
Switch
} from 'react-router-dom';
import './App.css';
import Navbar from './components/Navbar';
import Footer from './components/footer/Footer';
import { Routes } from './constants/Routes';
class App extends React.Component {
render() {
return (
<Router>
<div className="App">
<Navbar />
<Switch>
{ Routes }
</Switch>
<Footer />
</div>
</Router>
);
}
}
export default withRouter(App);
Routes.js
import React from 'react';
import { Route } from 'react-router-dom';
import Home from '../components/Home';
import Item from '../components/shopping/Item';
import Cart from '../components/cart/Cart';
import OrderStatus from '../components/footer/OrderStatus';
import GiftCards from '../components/footer/GiftCards';
import ReturnsExchanges from '../components/footer/Returns-Exchanges';
import Contact from '../components/footer/Contact';
export const Routes = <div>
<Route exact path="/" component={ Home } />
<Route path="/products" component={ Item }/>
<Route path="/cart" component={ Cart } />
<Route path="/order-status" component={ OrderStatus } />
<Route path="/gift-cards" component={ GiftCards } />
<Route path="/returns-exchanges" component={ ReturnsExchanges } />
<Route path="/contact" component={ Contact } />
</div>
One way to access history objects properties in any component, you need to wrap that component with withRouter.
import withRouter from react-router-dom and wrap your App component like this:
export default withRouter(App);
More on withRouter.
Also, in index.js, make sure your <App /> is wrapped inside <BrowserRouter>.
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>
, document.getElementById('root'));
EDIT:
TypeError: Cannot read property 'props' of undefined
Replace your constructor with the following code. When implementing a constructor for React Component super(props) should be before any other statement otherwise props will be undefined in constructor.
constructor(props){
super(props);
this.state = {
item: this.props.location.state
}
}
Error: TypeError: Cannot read property 'img' of null
Reason:
Initial value of the state should not be null. You may pass empty object {} but not null, in this case.
You're trying to access img, title, price and desc in Item.js but only passing id in state of <Link> in Home.js.
<Link to =
{{ pathname: `/products/${item.category}`,
search: `?id=${item.id}`,
state: {id: `${item.id}`} //pass img, title, desc, price etc as well
}}
component={ Item }>
<img src={item.img} alt={item.title} />
</Link>

How to link to Routes inside the component rendered by another Route with React Router

i'm trying to use react router in my reactjs app. And I encountered this problem:
This is what i'm trying to do:
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import About from '../Pages/About';
import Home from '../Pages/Home';
import Topics from '../Pages/Topics';
import LinkTest from '../Pages/LinkTest';
class AppRouter extends Component {
render() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/home">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
<Route path="/home" component={LinkTest}/>
</ul>
<hr />
<Route path="/home" component={Home}/>
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</Router>
);
}
}
export default AppRouter;
Ignore "about" and "topic" component, when I click on "Home" link, it should target 2 routes, one will render "LinkTest" and the other renders "Home".
This is what inside "LinkTest":
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class LinkTest extends Component {
render() {
const {match}=this.props;
return (
<div>
<Link to={`${match.url}/Test`}>Link To Test</Link>
</div>
);
}
}
export default LinkTest;
And inside my "Home" component:
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import Test from './Test';
class Home extends Component {
render() {
const {match} = this.props;
console.log(match.url);
return (
<Router>
<div>
<h2>
Hello from Home page
<Link to={`${match.url}/Test`}>Link To Test</Link>
<Route path={`${match.url}/Test`} component={Test}/>
</h2>
</div>
</Router>
);
}
}
export default Home;
However:
When i click on the link inside "LinkTest" component (which was rendered earlier), the url on the browser is shown "http://localhost:3000/home/Test", but nothing happens.
When i clicked on the link inside "Home" component, (which was rendered the same time as the "LinkTest" using the same link), it showed the same url on the browser: "http://localhost:3000/home/Test", only this time it rendered the "Test" component.
Why does this happen? (what i want to achieve is I want to use the link inside "LinkTest" to render "Test" component inside "Home" component, or something similar).
I hope this is clear enough.
You can do it in following way:
<Route exact path='/a' component={A} />
<Route path='/b' component={B} />
// Following should be router inside Component B
<Route exact path='/b/abc' component={OtherComponent}
If you want I've prepared few react-router 4 examples. These are hosted here. https://github.com/SandipNirmal/react-router-examples
If you need Nested routing inside ComponentB you should add Links for those Routes as well. And use match.url and match.path to build the nested Links and Routes.
const ComponentB = ({ match }) => {
return (
<div>
<div>
<ul>
<li><Link to={`${match.url}/c`}>Component C</Link></li>
// more Links
<li><Link to={`${match.url}/css`}>CSS</Link></li>
<li><Link to={`${match.url}/react`}>React</Link></li>
</ul>
</div>
<div>
<Route path={`${match.path}/c`} component={ComponentC} />
// more nested Routes
<Route path={`${match.path}/css`} render={() => { return <h1>CSS</h1> }}/>
<Route path={`${match.path}/react`} render={() => { return <h1>React</h1> }}/>
</div>
</div>
);
}
Nested routing
Components created via Route will automatically be passed the following prop objects: match, location and history.
Using match you can implement nested Routes. The match object contains the following properties:
params — (object) Key/value pairs parsed from the URL corresponding
to the dynamic segments of the path
isExact — (boolean) true if the entire URL was matched (no trailing characters)
path — (string) The path pattern used to match. Useful for building nested Routes
url — (string) The matched portion of the URL. Useful for building
nested Links
Reference
https://medium.freecodecamp.org/beginners-guide-to-react-router-4-8959ceb3ad58
https://medium.com/#pshrmn/a-simple-react-router-v4-tutorial-7f23ff27adf

How to pass props to a router?

I have a file route.js which has
import React from 'react'
import { render } from 'react-dom'
import {HashRouter ,Route,Link} from 'react-router-dom'
const Router = (props) => (
<HashRouter>
<div>
<Route exact path="/" render={() => <list data={props.data}/>}/>
<Route path="/list" render={() => <Table data={props.data}/>}/>
</div>
</HashRouter>
here is the edit page code-
import React, { Component} from 'react'
export default class Edit extends Component {
render() {
return (
<div>
<center>{this.props.data}</center>
</div>
);
}
}
i want to pass the list data to the next page.how can i pass props to page from router
If you want to pass any props to some route you can just pass it through the params prop of Link.
Here in your case you could just set a button anywhere on the FreindList page which should be like:
<Link to='edit' params={{data: props.data.name}}>
<button>Edit</button>
</Link>
Further you can access this array in the edit page aswel.
don not forget to import Link from react router
Since you are using react-router-dom which is react router 4, you can just use, something like this:
<Link to={{
pathname: '/edit',
state: { data: props.data.name }
}}>
<button>Edit</button>
</Link>
You will get it in this.props.location.state in the edit component.
For more documentation on react router 4 check this out

Link, activeClassName just plain not working

I cannot get activeClassName to render correctly in my Navigation component Link. This has been tracked down to the following symptoms:
In the following code, the Navigation component is not getting a props.route passed to it at all. The App component has a props.route, however it is never updated as the user navigates to other routes. It is always set to the first route that was loaded. The componentWillReceiveProps is fired when changing routes, as the props.children is changing.
Here are the relevant snippets of my files:
app.jsx
import router from 'app/router';
[...]
ReactDOM.render(
<Provider store={store}>
{router}
</Provider>,
document.getElementById('app')
);
router/index.jsx
export default (
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={GameBoard}/>
<Route path="profile" component={ProfileBoard}/>
<Route path="profile/:userId" component={ProfileBoard}/>
<Route path="help" component={Help}/>
<Route path="about" component={About}/>
</Route>
</Router>
);
App.jsx
import React from 'react';
import Navigation from 'Navigation';
export default class App extends React.Component {
static propTypes = {};
constructor(props) {
super(props);
}
render() {
return (
<div>
<Navigation />
<div className="content">
{this.props.children}
</div>
</div>
);
}
};
Navigation.jsx
import React from 'react';
import {connect} from 'react-redux';
import {IndexLink, Link} from 'react-router';
export class Navigation extends React.Component {
static propTypes = {};
constructor(props) {
super(props);
}
render() {
return (
<div className="top-bar navigation">
<div className="row">
<div className="small-12 columns">
<div className="top-bar-left">
<ul className="menu">
<li className="menu-text">
TVDeadpool.xyz
</li>
<li>
<IndexLink to="/" activeClassName="link-active">Bets</IndexLink>
</li>
<li>
<Link to="/help" activeClassName="link-active">Help</Link>
</li>
<li>
<Link to="/about" activeClassName="link-active">About</Link>
</li>
</ul>
</div>
</div>
</div>
</div>
);
}
}
export default connect()(Navigation);
Navigation.jsx has been simplified. I removed some functionality that shows a Logout link if you are logged in, and handles that link. That is the reason I am including connect, though.
I am pouring through the documentation for react-router but cannot for the life of me figure out where I am going wrong. It must be something to do with nesting within the <Provider/>, I guess? Any help would be appreciated!
Note that if you want to see this in (in)action, check out TVDeadpool.xyz. Not a plug, just a fact.
UPDATE
Here is a hack fix:
App.jsx
import React from 'react';
import Navigation from 'Navigation';
export default class App extends React.Component {
static propTypes = {};
constructor(props) {
super(props);
}
render() {
return (
<div>
<Navigation location={this.props.location.pathname}/>
<div className="content">
{this.props.children}
</div>
</div>
);
}
};
Simply adding that location prop to <Navigation/> causes a re-render, without any additional code.
I think the reason this is happening is that App is always considered to be at route.path of "/", no matter what route is actually showing. Its immediate children seem to get the appropriate route.path, but Navigation, being a nested component of App, does not. In fact, it does not receive a route prop at all because it is not directly referenced by a <Route/>.
That said, how would this ever work? Should you not be able to simple include Link and expect it to work as described? I feel like I am missing something key to how react-router is supposed to work.
I think the reason activeClassName is not working is because you are using connect
export default connect()(Navigation);
See this issue... https://github.com/reactjs/react-redux/issues/388
It is reported as fixed in React Router 3.0.0-alpha.1 and newer.
Another hack fix I found you can use in older versions of react router is to pass {pure : false} to tell connect it is not a pure component...
export default connect(mapStateToProps, null, null, { pure: false })(Navigation);

Resources