Reputation: 21314
I have the following structure for my React.js application using React Router:
var Dashboard = require('./Dashboard');
var Comments = require('./Comments');
var Index = React.createClass({
render: function () {
return (
<div>
<header>Some header</header>
<RouteHandler />
</div>
);
}
});
var routes = (
<Route path="/" handler={Index}>
<Route path="comments" handler={Comments}/>
<DefaultRoute handler={Dashboard}/>
</Route>
);
ReactRouter.run(routes, function (Handler) {
React.render(<Handler/>, document.body);
});
I want to pass some properties into the Comments
component.
(normally I'd do this like <Comments myprop="value" />
)
What's the easiest and right way to do so with React Router?
Upvotes: 395
Views: 333174
Reputation: 554
I have answered this already here.
Here are few ways you can pass props to a route component.
With the react-router v5, we can create routes by wrapping with a component, so that we can easily pass props to the desired component like this.
<Route path="/">
<Home name="Sai" />
</Route>
Similarly, you can use the children prop in v5.
<Route path="/" children={ <Home name="Sai" />} />
If you are using react-router v4, you can pass it using the render prop.
Side Note - Quoting from React router children-func doc
Sometimes you need to render whether the path matches the location or not. In these cases, you can use the function children prop. It works exactly like render except that it gets called whether there is a match or not.
<Route path="/" render={() => <Home name="Sai" />} />
(originally posted at https://reactgo.com/react-router-pass-props/)
Upvotes: 6
Reputation: 849
In react-router-v3, I do NOT find any working solutions, so I make a big trade-off, use class inherit instead of props.
for example:
class MyComments extends Comments{
constructor(props) {
super(props);
this.myProp = myValue;
}
}
and, you use MyComments
in Router's component
without props.
then, you can use this.myProp
to get 'myValue' in componentDidMount()
function;
Upvotes: 0
Reputation: 13044
The React Router v5.1 (React >= 16.8) way of doing this:
<Route path="/comments">
<Comments myprop="value" />
</Route>
Now if you want to access Route Props inside your component then you can refer this solution. In case of functional component, there is another hook useParams()
that is not mentioned in that post.
More reference: React Router v5.1
Upvotes: 1
Reputation: 997
Use the solution like a below and this works in v3.2.5.
<Route
path="/foo"
component={() => (
<Content
lang="foo"
meta={{
description: lang_foo.description
}}
/>
)}
/>
or
<Route path="/foo">
<Content
lang="foo"
meta={{
description: lang_foo.description
}}
/>
</Route>
Upvotes: 0
Reputation: 81
class App extends Component {
constructor(props){
super(props);
this.state = {
data:null
}
}
componentDidMount(){
database.ref().on('value', (snapshot) =>{
this.setState({
data : snapshot.val()
})
});
}
render(){
// const { data } = this.state
return (
<BrowserRouter>
<Switch>
<Route exact path = "/" component = { LandingPage } />
<Route
path='/signup'
render = { () => <Signup data = {this.state.data} />} />
</Switch>
</BrowserRouter>
);
}
};
export default App;
Upvotes: 0
Reputation: 3063
Since new release, it's possible to pass props directly via the Route
component, without using a Wrapper. For example, by using render
prop.
Component:
class Greeting extends React.Component {
render() {
const {text, match: {params}} = this.props;
const {name} = params;
return (
<React.Fragment>
<h1>Greeting page</h1>
<p>
{text} {name}
</p>
</React.Fragment>
);
}
}
Usage:
<Route path="/greeting/:name" render={(props) => <Greeting text="Hello, " {...props} />} />
My preferred way is wrap the Comments
component and pass the wrapper as a route handler.
This is your example with changes applied:
var Dashboard = require('./Dashboard');
var Comments = require('./Comments');
var CommentsWrapper = React.createClass({
render: function () {
return (
<Comments myprop="myvalue"/>
);
}
});
var Index = React.createClass({
render: function () {
return (
<div>
<header>Some header</header>
<RouteHandler/>
</div>
);
}
});
var routes = (
<Route path="/" handler={Index}>
<Route path="comments" handler={CommentsWrapper}/>
<DefaultRoute handler={Dashboard}/>
</Route>
);
ReactRouter.run(routes, function (Handler) {
React.render(<Handler/>, document.body);
});
Upvotes: 204
Reputation: 1864
this is probably the best way to use react-router-dom with a cookie handler
in index.js
import React, { Component } from 'react'
import {Switch,Route,Redirect} from "react-router-dom"
import {RouteWithLayout} from "./cookieCheck"
import Login from "../app/pages/login"
import DummyLayout from "../app/layouts/dummy"
import DummyPage from "../app/pages/dummy"
export default ({props})=>{
return(
<Switch>
<Route path="/login" component={Login} />
<RouteWithLayout path="/dummy" layout={DummyLayout} component={DummyPage}
{...props}/>
<Redirect from="/*" to="/login" />
</Switch>
)
}
and use a cookieCheck
import React , {createElement} from 'react'
import {Route,Redirect} from "react-router-dom"
import {COOKIE,getCookie} from "../services/"
export const RouteWithLayout = ({layout,component,...rest})=>{
if(getCookie(COOKIE)==null)return <Redirect to="/login"/>
return (
<Route {...rest} render={(props) =>
createElement(layout, {...props, ...rest}, createElement(component,
{...props, ...rest}))
}
/>
)
}
Upvotes: 0
Reputation: 1983
now there is a new way, to do this, although very similar to the previous method.
import { Match, Link, Miss } from 'react-router';
import Homepage from './containers/Homepage';
const route = {
exactly: true,
pattern: '/',
title: `${siteTitle} - homepage`,
component: Homepage
}
<Match { ...route } render={(props) => <route.component {...props} />} />
P.S. This works only in alpha version, and were removed after the v4 alpha release. In v4 latest, is once again , with the path and exact props.
react-lego an example app contains code that does exactly this in routes.js on its react-router-4 branch
Upvotes: 24
Reputation: 6397
This is the solution from Rajesh, without the inconvenient commented by yuji, and updated for React Router 4.
The code would be like this:
<Route path="comments" render={(props) => <Comments myProp="value" {...props}/>}/>
Note that I use render
instead of component
. The reason is to avoid undesired remounting. I also pass the props
to that method, and I use the same props on the Comments component with the object spread operator (ES7 proposal).
Upvotes: 65
Reputation: 71
React Router v 4 solution
I stumbled upon this question earlier today, and here is the pattern I use. Hopefully this is useful to anyone looking for a more current solution.
I'm not sure if this is the best solution, but this is my current pattern for this. I have typically have a Core directory where I keep my commonly used components with their relevant configurations (loaders, modals, etc), and I include a file like this:
import React from 'react'
import { Route } from 'react-router-dom'
const getLocationAwareComponent = (component) => (props) => (
<Route render={(routeProps) => React.createElement(component,
{...routeProps, ...props})}/>
)
export default getLocationAwareComponent
Then, in the file in question, I'll do the following:
import React from 'react'
import someComponent from 'components/SomeComponent'
import { getLocationAwareComponent } from 'components/Core/getLocationAwareComponent'
const SomeComponent = getLocationAwareComponent(someComponent)
// in render method:
<SomeComponent someProp={value} />
You'll notice I import the default export of my component as humble camel-case, which lets me name the new, location-aware component in CamelCase so I can use it normally. Other than the additional import line and the assignment line, the component behaves as expected and receives all its props normally, with the addition of all the route props. Thus, I can happily redirect from component lifecycle methods with this.props.history.push(), check the location, etc.
Hope this helps!
Upvotes: 7
Reputation: 186
You can also combine es6 and stateless functions to get a much cleaner result:
import Dashboard from './Dashboard';
import Comments from './Comments';
let dashboardWrapper = () => <Dashboard {...props} />,
commentsWrapper = () => <Comments {...props} />,
index = () => <div>
<header>Some header</header>
<RouteHandler />
{this.props.children}
</div>;
routes = {
component: index,
path: '/',
childRoutes: [
{
path: 'comments',
component: dashboardWrapper
}, {
path: 'dashboard',
component: commentsWrapper
}
]
}
Upvotes: 5
Reputation: 3567
Here's the cleanest solution I've come up with (React Router v4):
<Route
path="/"
component={props => <MyComponent {...props} foo="lol" />}
/>
MyComponent
still has props.match
and props.location
, and has props.foo === "lol"
.
Upvotes: 22
Reputation: 1693
Use the component with or without router based on Rajesh Naroth answer.
class Index extends React.Component {
constructor(props) {
super(props);
}
render() {
const foo = (this.props.route) ? this.props.route.foo : this.props.foo;
return (
<h1>
Index - {foo}
</h1>
);
}
}
var routes = (
<Route path="/" foo="bar" component={Index}/>
);
Or your could do it this way:
export const Index = ({foo, route}) => {
const content = (foo) ? foo : (route) ? route.foo : 'No content found!';
return <h1>{content}</h1>
};
Upvotes: 1
Reputation: 710
For react router 2.x.
const WrappedComponent = (Container, propsToPass, { children }) => <Container {...propsToPass}>{children}</Container>;
and in your routes...
<Route path="/" component={WrappedComponent.bind(null, LayoutContainer, { someProp })}>
</Route>
make sure the 3rd param is an object like: { checked: false }
.
Upvotes: 3
Reputation: 7355
Using a custom route component, this is possible in React Router v3.
var Dashboard = require('./Dashboard');
var Comments = require('./Comments');
var routes = (
<Route path="/" handler={Index}>
<MyRoute myprop="value" path="comments" handler={Comments}/>
<DefaultRoute handler={Dashboard}/>
</Route>
);
As for the <MyRoute>
component code, it should be something like:
import React from 'react';
import { Route } from 'react-router';
import { createRoutesFromReactChildren } from 'react-router/lib//RouteUtils';
const MyRoute = () => <div><MyRoute> elements are for configuration only and should not be rendered</div>;
MyRoute.createRouteFromReactElement = (element, parentRoute) => {
const { path, myprop } = element.props;
// dynamically add crud route
const myRoute = createRoutesFromReactChildren(
<Route path={path} />,
parentRoute
)[0];
// higher-order component to pass myprop as resource to components
myRoute.component = ({ children }) => (
<div>
{React.Children.map(children, child => React.cloneElement(child, { myprop }))}
</div>
);
return myRoute;
};
export default MyRoute;
For more details about the custom route component approach, check out my blog post on the subject: http://marmelab.com/blog/2016/09/20/custom-react-router-component.html
Upvotes: 0
Reputation: 761
Wrap it with a stateless function component:
<Router>
<Route
path='/'
component={({children}) =>
<MyComponent myProp={'myVal'}>{children}</MyComponent/>
}/>
</Router>
Upvotes: 13
Reputation: 1367
Copying from the comments by ciantic in the accepted response:
<Route path="comments" component={() => (<Comments myProp="value" />)}/>
This is the most graceful solution in my opinion. It works. Helped me.
Upvotes: 135
Reputation: 12037
Using ES6 you can just make component wrappers inline:
<Route path="/" component={() => <App myProp={someValue}/>} >
If you need to pass children:
<Route path="/" component={(props) => <App myProp={someValue}>{props.children}</App>} >
Upvotes: 25
Reputation: 1
for the react-router 2.5.2,the solution is so easy:
//someConponent
...
render:function(){
return (
<h1>This is the parent component who pass the prop to this.props.children</h1>
{this.props.children && React.cloneElement(this.props.children,{myProp:'value'})}
)
}
...
Upvotes: 0
Reputation: 1839
The problem with the React Router is that it renders your components and so stops you passsing in props. The Navigation router, on the other hand, lets you render your own components. That means you don't have to jump through any hoops to pass in props as the following code and accompanying JsFiddle show.
var Comments = ({myProp}) => <div>{myProp}</div>;
var stateNavigator = new Navigation.StateNavigator([
{key:'comments', route:''}
]);
stateNavigator.states.comments.navigated = function(data) {
ReactDOM.render(
<Comments myProp="value" />,
document.getElementById('content')
);
}
stateNavigator.start();
Upvotes: 1
Reputation: 762
In 1.0 and 2.0 you can use createElement
prop of Router
to specify how exactly to create your target element. Documentation source
function createWithDefaultProps(Component, props) {
return <Component {...props} myprop="value" />;
}
// and then
<Router createElement={createWithDefaultProps}>
...
</Router>
Upvotes: 9
Reputation: 3838
If you'd rather not write wrappers, I guess you could do this:
class Index extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<h1>
Index - {this.props.route.foo}
</h1>
);
}
}
var routes = (
<Route path="/" foo="bar" component={Index}/>
);
Upvotes: 272
Reputation: 1155
You can pass props by passing them to <RouteHandler>
(in v0.13.x) or the Route component itself in v1.0;
// v0.13.x
<RouteHandler/>
<RouteHandler someExtraProp={something}/>
// v1.0
{this.props.children}
{React.cloneElement(this.props.children, {someExtraProp: something })}
(from the upgrade guide at https://github.com/rackt/react-router/releases/tag/v1.0.0)
All child handlers will receive the same set of props - this may be useful or not depending on the circumstance.
Upvotes: 32
Reputation: 3237
Just a follow-up to ColCh's answer. It is quite easy to abstract the wrapping of a component:
var React = require('react');
var wrapComponent = function(Component, props) {
return React.createClass({
render: function() {
return React.createElement(Component, props);
}
});
};
<Route path="comments" handler={wrapComponent(Comments, {myprop: value})}/>
I haven't tested this solution yet so any feedback is important.
It's important to note that with this method, any props sent via the Router (such as params) get overwritten / removed.
Upvotes: 47
Reputation: 3794
You can pass in props via the <RouterHandler/>
like this:
var Dashboard = require('./Dashboard');
var Comments = require('./Comments');
var Index = React.createClass({
render: function () {
var props = this.props; // or possibly this.state
return (
<div>
<header>Some header</header>
<RouteHandler {...props} />
</div>
);
}
});
The downside of this is you are passing props indiscriminately. So Comments
may end up receiving props that are really intended for a different component depending on your routes configuration. It's not a huge deal since props
is immutable, but this can be problematic if two different components are expecting a prop named foo
but with different values.
Upvotes: 11
Reputation: 1084
You could also use the RouteHandler mixin to avoid the wrapper component and more easily pass down the parent's state as props:
var Dashboard = require('./Dashboard');
var Comments = require('./Comments');
var RouteHandler = require('react-router/modules/mixins/RouteHandler');
var Index = React.createClass({
mixins: [RouteHandler],
render: function () {
var handler = this.getRouteHandler({ myProp: 'value'});
return (
<div>
<header>Some header</header>
{handler}
</div>
);
}
});
var routes = (
<Route path="/" handler={Index}>
<Route path="comments" handler={Comments}/>
<DefaultRoute handler={Dashboard}/>
</Route>
);
ReactRouter.run(routes, function (Handler) {
React.render(<Handler/>, document.body);
});
Upvotes: 12