那样做)使用 React Router 最简单和正确的方法是什么?" /> 那样做)使用 React Router 最简单和正确的方法是什么?"> 那样做)使用 React Router 最简单和正确的方法是什么?" />
ChatGPT解决这个技术问题 Extra ChatGPT

react-router - pass props to handler component

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?

The problem here, and in such similar cases, especially with the frameworks or libs written in some langs, a certain lack of means of combination (MoC). Primitives seems ok in React they are pretty good, defining components with primitives, in React elements and the component, MoC, which seems ok as well in React. But means of combination is incomplete. One must be able to pass the props to a component while combining a component to another, doesn't matter if by putting one component inside another component as a child of it or passing one component as a props to another.
With some syntax like <ComponentA x={<ComponentB y={<ComponentC z={} />} />} /> OR <ComponentA x={ComponentB(ComponentC()) } /> Otherwise, this problems of combinations of abstractions will recur and will need some less than optimal and indirect solutions called workarounds like wrapping etc, etc. Abstractions must be first class citizens as primitives, whatever the first class perception means.

T
Thomas E

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}/>
);

This is a correct answer. In the react-router 1.0, you can get route plain object in your component. Here's the answer of github issue: github.com/rackt/react-router/issues/615#issuecomment-100432086
This is the simple answer I was looking for. The other techniques work, but require 10x the amount of code. Works well for v1.0.x. The only downside I can see is if you intend to use the same component both with and without the router container. But for me, all my top-level components mapped 1-to-1 with routes.
Great, thanks! If anyone is wondering, the foo property would be available in your component as: this.props.route.foo
Can this one be set as a correct answer to avoid the confusion?
Nope! See Rajesh Naroth answer for the real solution :)
d
daaawx

UPDATE

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} />} />

Codesandbox Example

OLD VERSION

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);
});

I'm running into the same problem, but doesn't this solution get verbose quickly?
Agree with captDaylight, it becomes verbose. Would prefer a better way to handle this!
@mattiashallstrom IMO, the better way in 1.0 is to simply add the property to the route. See Thomas E's answer.
you could add a stateless component syntax (just lambda) there also, it's pretty short <Route path="comments" component={() => (<Comments myProp="value" />)}/>
I will never agree with creating additional component just to pass properties being the "preferred way". It's verbose, complicated, error prone and just plainly wrong in every way imaginable. It might be the ONLY way react router allows, but calling it "preferred" is a stretch. Preferred by who?
C
Community

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.


This is basically the same as the wrapper answer above, but it's much less wordy. Man, though, that syntax sucks. Try throwing in a _ref
It's like a anonymous wrapper, so all injected props (eg. location) are missed. One has to manually pass props like component={(props) => (<Comments myProp="value" location={ props.location } />)} but it all gets messy again
@JacobThomason we don't re-render react router config so it's unlikely to be a performance penalty.
As of React-Router 4, if you provide an inline function you will get a lot of undesired remounting. For inline rendering, use the render prop. Link to the docs
@yuji To not make it too messy one can do: component={(props) => (<Comments {...props} myProp="value" />)} to maintain the injected props
D
Daniel Reina

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).


Really great with solving undesired mounting too! +1
C
Community

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.


Bob, are you familiar with closures? stackoverflow.com/questions/111102/…
And if you need the query and params from the router as well, then something like this will work: return React.createElement(Component, _.assign({}, this.props, props)); (This one uses _.assign to compose the combined object... other methods are available of course).
You might want to pass the children also.| var wrapComponent = function(Component, props) { return React.createClass({ render: function() { return React.createElement(Component, props, this.props.children); } }); };
This is a Higher Order Component for those who aren't familiar with them facebook.github.io/react/docs/higher-order-components.html
N.B. This is for an old version of React Router now. Current v4 has render, component and children methods for Route. Note that as @dgrcode answer points out, you should use render instead of component
c
cachvico

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.


It really puzzles to see React.cloneElement being passed multiple elements but the function signature seems to take only one react element. I think this snippet can be made easier to understand.
This is clearly the best answer on target with the docs, but I also agree with manu that it could be written to show use better. Code more specific to the question would look like: React.cloneElement(this.props.children, {myprop: "value"}) or React.cloneElement(this.props.children, {myprop: this.props.myprop}) etc.
This answer wins. It is also a lot more clear what's going on in the midst of the stuff Router does for you. As someone reading the code, if I know Comments is inside Index, I'll look at Index to see what props are sent to Comments. If I happen to know that Comments is a router handler, I'll look at the router config and find out that Index parents comments and still go look there.
N
Nick

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>} >


This is nice but it doesn't pass through the children in case there are any.
@zpr I've added an example for props.children
As @dgrcode answer points out, you should use render instead of component
T
Taras Yaremkiv

React-router v4 alpha

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


c
cgenco

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".


R
Rafa Viotti

Wrap it with a stateless function component:

<Router>
  <Route 
    path='/' 
    component={({children}) => 
      <MyComponent myProp={'myVal'}>{children}</MyComponent/>
    }/>
</Router>

j
jul

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);
});

It's publicly exposed on the global bower build as ReactRouter.RouteHandlerMixin so I don't think so.
This also allows for animating transitions using TransitionGroup and CSSTransitionGroup which I couldn't get to work using the wrapper method.
It's strange that there's no mention about that in official docs.
Mixins are no longer recommended by the React docs: facebook.github.io/react/blog/2016/07/13/…
M
Meistro

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.


What do the 3 periods/dots do or mean in this code: {...props}
I guess Flux would avoid the need to send state from the parent App down to a route. I got the above code working, but it's not explicit so the hidden majic is ugly, not easy to trace and track what's going on.
Spread operator explanation. It's not explicit but, it's not the worst thing since you are passing props which are immutable.
this worked for me but the correct syntax is {...this.props}
A
Andrew Khmylov

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>

C
Chris

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!


S
Subham Tripathi

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/)


i
icc97

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
      }
    ]
}

I'm not sure exactly of how this works - but it looks wrong. You're using this.props in a function, which I'm pretty sure won't work. If you're using pure functions instead of extending the React.Component then you have to pass in props as an argument, see the React docs on Components and Props
h
holyxiaoxin

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 }.


g
graham mendick

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();

M
Michael Hobbs

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>
};

b
brc-dd

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


⚠ CAUTION: You will loose access to <Route props />. The only we can both have the custom props along with the route props is using the <Route render/> - render props like this: <Route path="/comments" render={(props) => <Comments myProp="value" {...props}/>}/>.
@Mr.spShuvo you are wrong. I have updated my answer. Check that out. You just need withRouter in case of class components, or need hooks in case of functional components.
Ok, may be I'm partially wrong. But with class components, you cannot use hooks. Thanks for the update and more reference link.
m
min may

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'})}
  )
}
...

F
François Zaninotto

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>&lt;MyRoute&gt; 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


S
Snivio

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}))
       }
      />
    )
}

n
nik.ss
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;

i
illvart

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>

K
Kai

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;