react-router - 将道具传递给处理程序组件

react-router - pass props to handler component

我的 React.js 应用程序使用 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);
});

我想将一些属性传递给 Comments 组件。

(通常我会这样做 <Comments myprop="value" />

使用 React Router 最简单、最正确的方法是什么?

更新

自新版本发布以来,可以直接通过 Route 组件传递道具,而无需使用 Wrapper。例如,通过 using render prop.

分量:

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

用法:

<Route path="/greeting/:name" render={(props) => <Greeting text="Hello, " {...props} />} />

Codesandbox Example


旧版本

我的首选方法是包装 Comments 组件并将包装器作为路由处理程序传递。

这是应用了更改的示例:

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

您还可以使用 RouteHandler mixin 来避免包装器组件,并更轻松地将父级的状态作为 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);
});

你可以像这样通过 <RouterHandler/> 传递道具:

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

这样做的缺点是你不加区别地传递道具。因此 Comments 最终可能会收到真正用于不同组件的道具,具体取决于您的路由配置。这没什么大不了的,因为 props 是不可变的,但是如果两个不同的组件需要一个名为 foo 但具有不同值的道具,这可能会有问题。

只是 ColCh 回答的后续。抽象组件的包装非常容易:

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

我还没有测试过这个解决方案,所以任何反馈都很重要。

请务必注意,使用此方法时,通过路由器发送的任何道具(例如参数)都会被覆盖/删除。

您可以将 props 传递给 <RouteHandler>(在 v0.13.x 中)或在 v1.0 中传递给 Route 组件本身;

// v0.13.x
<RouteHandler/>
<RouteHandler someExtraProp={something}/>

// v1.0
{this.props.children}
{React.cloneElement(this.props.children, {someExtraProp: something })}

(来自 https://github.com/rackt/react-router/releases/tag/v1.0.0 的升级指南)

所有子处理程序都将收到同一组道具 - 这可能有用也可能没用,具体取决于具体情况。

如果您不想编写包装器,我想您可以这样做:

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

您还可以结合使用 es6 和 stateless functions 以获得更清晰的结果:

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

在 1.0 和 2.0 中,您可以使用 RoutercreateElement 属性来指定如何创建目标元素。 Documentation source

function createWithDefaultProps(Component, props) {
    return <Component {...props} myprop="value" />;
}

// and then    
<Router createElement={createWithDefaultProps}>
    ...
</Router>

用无状态函数组件包裹它:

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

React Router 的问题在于它会渲染您的组件,因此会阻止您传入 props。 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 演出。

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

对于react-router 2.5.2,解决方案很简单:

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

使用 ES6,您可以将组件包装器内联:

<Route path="/" component={() => <App myProp={someValue}/>} >

如果需要通过children:

<Route path="/" component={(props) => <App myProp={someValue}>{props.children}</App>} >

ciantic 接受的回复中的评论中复制:

<Route path="comments" component={() => (<Comments myProp="value" />)}/>

这是我认为最优雅的解决方案。有用。帮了我。

React-router v4 alpha

现在有一种新方法可以做到这一点,尽管它与以前的方法非常相似。

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。这仅适用于 alpha 版本,并在 v4 alpha 版本后被删除。在最新的 v4 中,再次出现,带有路径和确切的道具。

react-lego an example app contains code that does exactly this in routes.js on its react-router-4 branch

使用自定义路由组件,这在 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>
);

至于<MyRoute>组件代码,应该是这样的:

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;

有关自定义路由组件方法的更多详细信息,请查看我关于该主题的博客 post:http://marmelab.com/blog/2016/09/20/custom-react-router-component.html

对于 React 路由器 2.x.

const WrappedComponent = (Container, propsToPass, { children }) => <Container {...propsToPass}>{children}</Container>;

在你的路线中...

<Route path="/" component={WrappedComponent.bind(null, LayoutContainer, { someProp })}>
</Route>

确保第三个参数是一个像这样的对象:{ checked: false }.

根据 Rajesh Naroth 的回答使用带或不带路由器的组件。

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

或者你可以这样做:

export const Index = ({foo, route}) => {
  const content = (foo) ? foo : (route) ? route.foo : 'No content found!';
  return <h1>{content}</h1>
};

这是我提出的最干净的解决方案(React Router v4):

<Route
  path="/"
  component={props => <MyComponent {...props} foo="lol" />}
/>

MyComponent还有props.matchprops.location,还有props.foo === "lol".

这是 , without the inconvenient ,并针对 React Router 4 进行了更新。

代码应该是这样的:

<Route path="comments" render={(props) => <Comments myProp="value" {...props}/>}/>

请注意,我使用 render 而不是 component。原因是为了避免undesired remounting。我还将 props 传递给该方法,并在带有对象展开运算符(ES7 提案)的 Comments 组件上使用相同的道具。

React Router v 4 解决方案

我今天早些时候偶然发现了这个问题,这是我使用的模式。希望这对寻找更新解决方案的任何人都有用。

我不确定这是否是最好的解决方案,但这是我目前的模式。我通常有一个 Core 目录,我在其中保存常用组件及其相关配置(加载器、模态等),并且我包含一个这样的文件:

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

然后,在相关文件中,我将执行以下操作:

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

您会注意到我将组件的默认导出导入为不起眼的驼峰命名法,这让我可以在 CamelCase 中命名新的位置感知组件,以便我可以正常使用它。除了额外的导入行和分配行之外,该组件的行为符合预期并正常接收其所有道具,并添加了所有路由道具。因此,我可以愉快地使用 this.props.history.push() 从组件生命周期方法重定向,检查位置等

希望对您有所帮助!

这可能是将 react-router-dom 与 cookie 处理程序一起使用的最佳方式

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

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

使用下面的解决方案,这在 v3.2.5 中有效。

<Route
  path="/foo"
  component={() => (
    <Content
      lang="foo"
      meta={{
        description: lang_foo.description
      }}
    />
  )}
/>

<Route path="/foo">
  <Content
    lang="foo"
    meta={{
      description: lang_foo.description
    }}
  />
</Route>

我已经回答了这个问题

这里有几种方法可以将 props 传递给路由组件。

使用 react-router v5,我们可以通过组件包装来创建路由,这样我们就可以像这样轻松地将 props 传递给所需的组件。

<Route path="/">
    <Home name="Sai" />
</Route>

同样,你可以使用v5中的children属性

<Route path="/" children={ <Home name="Sai" />} />

如果您使用的是 react-router v4,您可以使用 render 属性传递它。

旁注 - 引用自 React 路由器 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" />} />

(最初发布于 https://reactgo.com/react-router-pass-props/

React Router v5.1 (React >= 16.8) 这样做的方式:

<Route path="/comments">
    <Comments myprop="value" />
</Route>

现在,如果您想访问 post 中未提及的 Route Props inside your component then you can refer this . In case of functional component, there is another hook useParams()

更多参考:React Router v5.1

在 react-router-v3 中,我没有找到任何可行的解决方案,所以我做了一个很大的权衡,使用 class inherit 而不是 props。

例如:

class MyComments extends Comments{
  constructor(props) {
    super(props);
    this.myProp = myValue;
  }
}

而且,你在 Router 的 component 中使用 MyComments 没有道具。

然后,你可以在componentDidMount()函数中使用this.myProp得到'myValue';