I'm trying to figure out how to pass React Router's location prop to a component.
I have a route defined like so:
<Route
path="placeholder"
render={(props) => <Navigation {...props} />}
/>
In my Navigation component I do console.log(this.props); in the render method only to get an empty object. What gives? Isn't the location prop supposed to be automatically supplied to any component inside a Route?
By the way, I'm using react-router-dom version 4.2.2.
You need to wrap your component with withRouter in order to inject match, history and location in your component props.
import { withRouter } from 'react-router-dom';
class Navigation extends React.Component {
render() {
const { match, location, history } = this.props
return (
<div>{location.pathname}</div>
)
}
}
export default withRouter(Navigation)
You need to use the withRouter function from 'react-router-dom' on you main component where you setup Routes wrapped in a Switch and you should have access to location prop on Navigation Component by using this.props.location
App.js
Class App extends Component {
render(){
return (
<Aux>
<Switch>
<Route path="/login" exact component={Login}/>
<Route path="/Navigation" component={Navigation}/>
<Redirect to="/login"/>
</Switch>
</Aux>
);
}
}
export default withRouter(App);
Related
I'm upgrading react router to react router v6. In the newest version withRouter is not supported anymore. I implemented this wrapper as a replacement.
export const withRouter = Component => {
const Wrapper = props => {
const navigate = useNavigate();
return <Component navigate={navigate} {...props} />;
};
return Wrapper;
};
I implemented it like that
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps
)(Layout)
);
basically the wrapper is at the same spot as the original withRouter was in the application.
Currently I get the error
Uncaught Error: [Wrapper] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
I exchanged <Component navigate={navigate} {...props}/> with <Route navigate={navigate} {...props}/>, however I still get the same error. Thanks!
Wrapper is the returned component that you appear to be trying to render as a route component in a Routes component, but as the error points out, this is an invariant violation.
Given:
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps
)(Layout)
);
It seems you are trying to directly render this component in the Routes component, something like:
<Routes>
<DecoratedLayout> // <-- Not a Route component!!
<Route ....... />
...
</DecoratedLayout>
</Routes>
This is invalid though. In this example the DecoratedLayout component should be rendered as a Wrapper component (renders children prop) or on a Layout Route (renders Outlet component).
As a Wrapper component:
<Routes>
<Route
path="...."
element={(
<DecoratedLayout>
...
</DecoratedLayout>
)}
/>
...
</Routes>
As a Layout Route:
<Routes>
<Route element={<DecoratedLayout />}>
<Route ....... />
...
</Route>
...
</Routes>
I use react-hook in my all component. Now when I want to render It's inside React_Router BrowserRouter component It's given me an error.
Error Massage: Invalid hook call. Hooks can only be called inside of the body of a function component
If I understand well your problem, I think you could do nested routes with hoo like it.
Here would be your main router for example:
import React from 'react';
import {Switch, Route, withRouter, Link} from "react-router-dom";
import MyComponent from "./MyComponent";
class Main extends React.Component
{
render() {
return (
<div className='main'>
<Switch>
<Route exact path='/test' component={MyComponent} />
</Switch>
</div>
);
}
}
export default withRouter(Main);
And Here would be your routed component with nested route.
import React from 'react';
import {Switch, Route, withRouter} from "react-router-dom";
class MyComponent extends React.Component
{
render()
{
const {path} = this.props.match;
return (
<div className='test'>
<Switch>
<Route path={`${path}/catalog`}>
<div>Route catalog</div>
</Route>
<Route exact path={path}>
<div>Route dashboard</div>
</Route>
</Switch>
</div>
);
}
}
export default withRouter(MyComponent);
I had the same error in the past days. In my case the problem was that i use render prop of Route component instead of component prop
<Route render={FunCompWithHooks} /> {/* wrong */}
<Route component={FunCompWithHooks} /> {/* correct */}
I'm using a simple router in React
<Router>
<div>
<Switch>
<Route path="/" component={ Home } exact />
<Route path="/contact" component={ Contact } />
<Route path="/:slug" component={ Post } />
</Switch>
</div>
</Router>
I'm pulling posts from a blog using REST and have a router component named Post for single blog posts. Any Route that doesn't match with home or contact, uses the post component.
How can I get or pass the route slug/url segment in the Post component? For example if the url segment/slug is /some-blog-post-title, I want to retrieve it, preferably using a React Router function/method if it exists.
You can get the parameters in the props.match.params object. To get your :slug parameter you would write props.match.params.slug.
Example
class Post extends React.Component {
componentDidMount() {
this.getPost(this.props.match.params.slug);
}
componentDidUpdate(prevProps) {
if (prevProps.match.params.slug !== this.props.match.params.slug) {
this.getPost(this.props.match.params.slug);
}
}
getPost = slug => {
// ...
};
render() {
return <h2>{this.props.match.params.slug}</h2>;
}
}
If you want to grab urls in a functional component
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import User from './User';
class App extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path='/user/:userName' component={User} />
<Route>
<div>Default page</div>
</Route>
</Switch>
</Router>
);
}
}
export default App;
Inside the functional component you can grab it
import React from 'react';
import { useParams } from 'react-router-dom';
const User = () => {
const { userName } = useParams();
return (
<div>Username: { userName }</div>
);
}
export default User;
I started to experiment with react router, and dynamic matches.
I wanted to create a function which matches the slug of the URL to a slug in a JSON file.
The error I get:
TypeError: Unable to get property 'slug' of undefined or null reference
I think that the 'Slug' of the url is undefined, but I am not sure on how to fix it.
screenshot of error
my code for routes.js:
import React from 'react';
import Header from './components/header/header.js';
import Home from './components/home/home.js';
import About from './components/about/about.js';
import NotFound from './components/notFound/notFound.js'
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import PostPage from './components/postpage/postpage.js'
import posts from './files/data.json';
class Routes extends React.Component {
render(){
return (
<Router>
<div>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Route path="/:slug" component={props => {
const postt = posts.posts.filter (post => props.params.slug === post.slug)
console.log(postt.length)
return <PostPage post={postt} />
} } />
}}/>
<Route component={NotFound} />
</Switch>
</div>
</Router>
);
}
}
export default Routes;
PostsPage.js:
import React from 'react';
import Post from '../post/post.js'
const PostPage = (props) => (
<div>
<Post {...props.post}/>
</div>
);
export default PostPage;
and posts.js:
import React from 'react';
import { Link } from 'react-router-dom';
import './post.css';
class Post extends React.Component {
render(){
return(
<div>
<div >
<h2 className='subTitle'><Link to={`/post/${this.props.slug}`} className='link'>{this.props.title}</Link></h2>
<p className='content'>{this.props.excerpt}</p>
</div>
</div>
);
}
}
export default Post;
If you made it this far thank you for helping
slug variable is given inside match props which you are missing.
<Route path="/:slug" render={props => {
const postt = posts.posts.filter (post => props.match.params.slug === post.slug)
console.log(postt.length)
return <PostPage post={postt} />
} } />
}}/>
Also, do not inline component use a render function instead. From the docs:
When you use component (instead of render or children, below) the
router uses React.createElement to create a new React element from the
given component. That means if you provide an inline function to the
component prop, you would create a new component every render. This
results in the existing component unmounting and the new component
mounting instead of just updating the existing component. When using
an inline function for inline rendering, use the render or the
children prop (below).
https://reacttraining.com/react-router/web/api/Route/render-func
One of the ways you can get this fixed is by using .find() instead of .filter() like this :
const postt = posts.find (post => props.match.params.slug === post.slug)
And then inside your <Router /> make sure to send the rest of {...props} as well :
<Route path="/:slug" component={props => {
const postt = posts.find (post => props.match.params.slug === post.slug)
console.log(postt.length)
return <PostPage post={postt} {...props} />
} } />
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.