对于使用React Router的React.js应用程序,我具有以下结构:

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这样做最简单,最正确的方法是什么?

评论

这里的问题,在类似的情况下,尤其是用某些lang编写的框架或库,显然缺乏组合手段(MoC)。在React中,原语似乎还不错,它们在React元素中使用原语定义组件,而组件MoC在React中似乎也不错。但是结合的手段是不完整的。一个组件必须能够将道具传递给一个组件,同时又将一个组件与另一个组件组合在一起,这无关紧要,方法是将一个组件作为另一个组件的子组件放置在另一个组件中,或者将一个组件作为道具传递给另一个组件。

使用某些语法,例如} />} />或否则,将出现以下问题:抽象将会复现,并且需要一些非最佳和间接的解决方案,称为解决方法,例如包装等。抽象化必须是一流公民作为基元,无论一流感知的含义如何。

#1 楼

UPDATE

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

组件:

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示例


旧版本

我的首选方法是包装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);
});


评论


我遇到了同样的问题,但是这种解决方案不是很快变得冗长吗?

– captDaylight
2015年5月5日23:17

同意captDaylight,它变得冗长。宁愿有更好的方法来解决这个问题!

–马提亚斯·霍尔斯特罗姆(MattiasHallström)
15年3月11日在8:31

@mattiashallstrom IMO,1.0中更好的方法是简单地将属性添加到路由。请参阅Thomas E的答案。

–k00k
16年1月8日在17:20

您还可以在其中添加无状态组件语法(仅lambda),它非常短)}} />

–中国
16年6月17日19:55



我永远不会同意仅通过传递“首选方式”的属性来创建其他组件。它是冗长,复杂,容易出错的,并且以各种可以想象的方式都是明显错误的。这可能是React Router允许的唯一方式,但是将其称为“ preferred”是很麻烦的。被谁偏爱?

– SzczepanHołyszewski
16-6-29在15:25



#2 楼

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

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


评论


这是正确的答案。在react-router 1.0中,您可以在组件中获取路由普通对象。这是github问题的答案:github.com/rackt/react-router/issues/615#issuecomment-100432086

– ycdesu
15/09/26在3:25



这是我一直在寻找的简单答案。其他技术也可以,但是需要的代码量是其的10倍。适用于v1.0.x。我唯一能看到的缺点是,无论您打算使用带有或不带有路由器容器的相同组件。但是对我来说,我所有的顶级组件都通过路由一对一映射。

–killthrush
16年1月4日14:14



万分感谢!如果有人想知道,则foo属性将在您的组件中以如下形式提供:this.props.route.foo

–k00k
16年8月8日在17:12

可以将此设置为正确答案以避免混淆吗?

– Archibald
16年4月8日在20:49

不!真正的解决方案请参见Rajesh Naroth的答案:)

– Alex
16年8月27日在3:24

#3 楼

复制ciantic在接受的答复中的评论:

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


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

评论


这基本上与上面的包装器答案相同,但是少了很多麻烦。不过,那句语法很烂。尝试抛出_ref

– EdH
16-10-3在17:34

这就像一个匿名包装,因此所有注入的道具(例如位置)都将丢失。必须手动传递诸如component = {(props)=>())}之类的道具,但一切又变得混乱了

– yuji
16-10-17在11:01



@JacobThomason我们不会重新渲染反应路由器配置,因此不太可能会降低性能。

–塞巴斯蒂安·洛伯(Sebastien Lorber)
16年7月7日在14:21

从React-Router 4开始,如果您提供一个内联函数,您将获得许多不希望的重新安装。对于内联渲染,请使用渲染道具。链接到文档

–丹尼尔·雷纳(Daniel Reina)
17年4月8日在19:26

@yuji为了避免过于混乱,可以执行以下操作:component = {(props)=>())}来维护注入的道具

–apelsinapa
17年8月16日在12:26



#4 楼

这是Rajesh提供的解决方案,没有yuji的不便之处,并且已针对React Router 4进行了更新。

代码如下:

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


请注意,我使用的是render而不是component。原因是避免不必要的重新安装。我还将props传递给该方法,并且在Comment组件上使用与对象散布运算符相同的道具(ES7提案)。

评论


真的也可以解决不必要的安装问题! +1

–GLindqvist
17年11月26日在21:51

#5 楼

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


我尚未测试此解决方案,因此任何反馈都很重要。

请务必注意,使用此方法,通过路由器发送的所有道具(例如params)都将被覆盖/删除。

评论


鲍勃,您熟悉闭包吗? stackoverflow.com/questions/111102/…

–西格马斯
2015年6月5日15:50



而且,如果您还需要路由器的查询和参数,那么类似的方法将起作用:return React.createElement(Component,_.assign({},this.props,props))); (此方法使用_.assign组成组合的对象...当然可以使用其他方法)。

–马尔科姆·德威尔(Malcolm Dwyer)
2015年9月1日在21:33



您可能还想让孩子通过。| var wrapComponent = function(Component,props){return React.createClass({render:function(){return React.createElement(Component,props,this.props.children);}}); };

–朱利奥·罗德里格斯(Julio Rodrigues)
2015年9月13日在1:13



对于不熟悉它们的人来说,这是一个高阶组件。facebook.github.io/ react / docs / higher-order-components.html

–icc97
17年5月27日在19:13

N.B.现在这是旧版本的React Router。当前v4具有Route的render,component和child方法。请注意,正如@dgrcode答案指出的那样,您应该使用render而不是component

–icc97
17年5月27日在20:39

#6 楼

您可以通过将道具传递到<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)

所有子处理程序都将收到相同的道具集-这可能有用吗?情况。

评论


看到React.cloneElement传递了多个元素确实让人感到困惑,但是函数签名似乎只包含一个react元素。我认为此片段可以更容易理解。

–手册
15-10-10在19:38



显然,这是针对文档的最佳答案,但我也同意手册中的建议,即可以更好地显示用法。该问题的特定代码如下所示:React.cloneElement(this.props.children,{myprop:“ value”})或React.cloneElement(this.props.children,{myprop:this.props.myprop})等。

–华尼托甘
16-2-26在22:24

这个答案胜出。 Router会为您执行的操作中发生的事情也更加清楚了。当有人在阅读代码时,如果我知道Comment包含在Index中,我将查看Index以查看将什么道具发送到Comments。如果我碰巧知道Comments是一个路由器处理程序,那么我将查看路由器配置,并找出Index父代注释,然后继续查看。

–mjohnsonengr
16 Mar 28 '16 at 20:53

#7 楼

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

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

如果需要传递子级:

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

评论


很好,但是如果有的话它不会通过孩子。

– zpr
16年8月14日,0:56

@zpr我为props.children添加了一个示例

–尼克
16年8月14日在23:19

正如@dgrcode答案指出的那样,您应该使用render而不是component

–icc97
17年5月27日在20:41

#8 楼

React-router v4 alpha


现在,有一种新方法可以做到这一点,尽管与以前的方法非常相似。
br /> PS该功能仅在Alpha版本中有效,并且在v4 Alpha版本后已删除。在最新的v4版本中,它再次是,带有路径和确切的道具。 >

#9 楼

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

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


MyComponent仍然具有props.matchprops.location,并且具有props.foo === "lol"

#10 楼

用无状态功能组件包装它:

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


#11 楼

您还可以使用RouteHandler mixin来避免包装器组件,并更容易传递作为道具的父级状态:

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


评论


它作为ReactRouter.RouteHandlerMixin在全球Bower构建上公开公开,所以我不这么认为。

– jul
15年1月28日在16:37

这也允许使用TransitionGroup和CSSTransitionGroup对过渡进行动画处理,而我使用wrapper方法无法工作。

– jul
15年1月28日在16:41

奇怪的是,官方文档中没有提到这一点。

–科斯梅蒂卡
15年1月28日在18:34

React文档不再推荐使用Mixins:facebook.github.io/react/blog/2016/07/13/…

–icc97
17年5月27日在19:16



#12 楼

您可以通过<RouterHandler/>传递道具,如下所示:因此,Comments可能最终会收到实际上用于其他组件的道具,具体取决于您的路线配置。由于props是不可变的,所以这并不是什么大问题,但是如果两个不同的组件期望一个名为foo的prop,但是具有不同的值,则可能会出现问题。

评论


这三个句点/点在此代码中的作用或含义:{... props}

–巨麋
2015年4月4日2:00

我想Flux可以避免将父App的状态发送到路由的需要。我使上面的代码正常工作,但是它不是很明确,因此隐藏的代码很丑陋,不易跟踪和跟踪正在发生的事情。

–巨麋
2015年4月4日在2:09

传播操作员说明。这不是很明确,但这并不是最坏的事情,因为您传递的是不可变的道具。

– Meistro
2015年4月4日在4:26

这是有关散布运算符的ReactJS文档:facebook.github.io/react/docs/jsx-spread.html以及facebook.github.io/react/docs/transferring-props.html

–巨麋
2015年4月7日在2:15



这对我有用,但是正确的语法是{... this.props}

–赢
2015年7月9日在14:06

#13 楼

在1.0和2.0中,可以使用createElementRouter属性来指定如何精确地创建目标元素。文档来源

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

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


#14 楼

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()从组件生命周期方法愉快地重定向,检查位置等。

希望这会有所帮助!

#15 楼

您还可以结合使用es6和无状态函数来获得更清晰的结果:

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


评论


我不确定这是如何工作的-但看起来不对。您正在函数中使用this.props,我敢肯定这是行不通的。如果您使用纯函数而不是扩展React.Component,则必须传递prop作为参数,请参阅关于Components和Props的React文档

–icc97
17年5月27日在19:24

#16 楼

对于React Router 2.x.

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


以及在您的路线中...

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


确保第三个参数是一个类似{ checked: false }的对象。

#17 楼

我已经在这里回答了。
有几种方法可以将props传递给路由组件。
使用react-router v5,我们可以通过包装组件来创建路由,以便我们轻松实现将道具传递到所需的组件。
 <Route path="/">
    <Home name="Sai" />
</Route>
 

同样,您可以在v5中使用子道具。
 <Route path="/" children={ <Home name="Sai" />} />
 

如果您使用react-router v4,则可以使用渲染道具传递它。
 <Route path="/" render={() => <Home name="Sai" />} />
 

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

#18 楼

React Router的问题在于它渲染了您的组件,因此阻止了您传递道具。另一方面,导航路由器使您可以渲染自己的组件。这意味着您无需跳过任何障碍即可传递道具,如以下代码和随附的JsFiddle所示。

#19 楼

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

#20 楼

使用如下所示的解决方案,该解决方案可在v3.2.5中使用。 lang-js prettyprint-override“> <Route path="/foo" component={() => ( <Content lang="foo" meta={{ description: lang_foo.description }} /> )} />

#21 楼

React Router v5.1(反应> = 16.8)的执行方式:
<Route path="/comments">
    <Comments myprop="value" />
</Route>

现在,如果要访问组件内部的Route Props,则可以参考此解决方案。如果是功能组件,则该帖子中未提及另一个钩子useParams()

评论


⚠注意:您将失去对的访问权限。唯一可以同时拥有自定义道具和路线道具的就是使用-渲染道具,如下所示: } />。

–spShuvo先生
20 Dec 30'0:37

@ Mr.spShuvo你错了。我已经更新了答案。检查出。对于类组件,只需要withRouter;对于功能组件,只需要钩子即可。

–brc-dd
20/12/30在11:17

好吧,可能是我部分错了。但是对于类组件,您不能使用钩子。感谢您的更新和更多参考链接。

–spShuvo先生
20 Dec 30 '13:27

#22 楼

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


#23 楼

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


有关自定义路由组件方法的更多详细信息,请参阅我的博客文章: http://marmelab.com/blog/2016/09/20/custom-react-router-component.html

#24 楼

这可能是将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}))
       }
      />
    )
}


#25 楼

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;