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

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

我的 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 最简单和正确的方法是什么?

这里的问题是,在类似的情况下,尤其是用某些语言编写的框架或库,缺乏组合方式(MoC)。 Primitives 在 React 中似乎还不错,它们非常好,在 React 元素和组件 MoC 中使用基元定义组件,这在 React 中似乎也不错。但是组合的手段是不完整的。一个人必须能够在将一个组件组合到另一个组件时将道具传递给一个组件,无论是将一个组件作为它的子组件放在另一个组件中还是将一个组件作为道具传递给另一个组件都没有关系。
使用诸如 <ComponentA x={<ComponentB y={<ComponentC z={} />} />} /><ComponentA x={ComponentB(ComponentC()) } /> 之类的语法 否则,这种抽象组合问题 将再次出现,并且需要一些不是最佳的间接解决方案,称为变通方法,如包装等。必须首先使用抽象阶级公民是原始人,无论第一阶级的感知是什么意思。

T
Thomas E

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

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 中,您可以在组件中获取 route 普通对象。这是 github 问题的答案:github.com/rackt/react-router/issues/615#issuecomment-100432086
这是我一直在寻找的简单答案。其他技术有效,但需要 10 倍的代码量。适用于 v1.0.x。我能看到的唯一缺点是,如果您打算在有和没有路由器容器的情况下使用相同的组件。但对我来说,我所有的顶级组件都与路线一一对应。
太谢谢了!如果有人想知道, foo 属性将在您的组件中可用:this.props.route.foo
可以将此设置为正确答案以避免混淆吗?
没有!有关真正的解决方案,请参阅 Rajesh Naroth 的答案:)
d
daaawx

更新

从新版本开始,可以直接通过 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);
});

我遇到了同样的问题,但是这个解决方案不会很快变得冗长吗?
同意 captDaylight,它变得冗长。希望有更好的方法来处理这个!
@mattiashallstrom IMO,1.0 中更好的方法是简单地将属性添加到路由中。请参阅 Thomas E 的回答。
你也可以在那里添加一个无状态组件语法(只是 lambda),它很短<Route path="comments" component={() => (<Comments myProp="value" />)}/>
我永远不会同意创建额外的组件只是为了传递属性是“首选方式”。它冗长、复杂、容易出错,而且在所有可以想象的方面都明显是错误的。这可能是反应路由器允许的唯一方式,但称其为“首选”是一种延伸。谁的首选?
C
Community

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

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

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


这与上面的包装答案基本相同,但它不那么罗嗦。不过,伙计,这种语法很糟糕。尝试加入 _ref
它就像一个匿名包装器,所以所有注入的道具(例如位置)都被遗漏了。必须手动传递像 component={(props) => (<Comments myProp="value" location={ props.location } />)} 这样的道具,但一切又变得一团糟
@JacobThomason 我们不会重新渲染反应路由器配置,因此不太可能导致性能损失。
从 React-Router 4 开始,如果你提供一个内联函数,你会得到很多不需要的重新挂载。对于内联渲染,使用 render 属性。 Link to the docs
@yuji 为了不让它太乱,可以做:component={(props) => (<Comments {...props} myProp="value" />)} 来维护注入的道具
D
Daniel Reina

这是 solution from Rajesh,没有不方便的 commented by yuji,并针对 React Router 4 进行了更新。

代码将是这样的:

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

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


也非常适合解决不受欢迎的安装问题! +1
C
Community

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

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

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


Bob,你熟悉闭包吗? stackoverflow.com/questions/111102/…
如果您还需要来自路由器的查询和参数,那么这样的事情将起作用:return React.createElement(Component, _.assign({}, this.props, props));(这个使用 _.assign 来组成组合对象......当然还有其他方法)。
您可能还想通过孩子。| var wrapComponent = function(Component, props) { return React.createClass({ render: function() { return React.createElement(Component, props, this.props.children); } }); };
对于不熟悉它们的人来说,这是一个高阶组件facebook.github.io/react/docs/higher-order-components.html
NB 这适用于旧版本的 React Router。当前 v4 具有用于 Routerendercomponentchildren 方法。请注意,正如 @dgrcode answer 指出的那样,您应该使用 render 而不是 component
c
cachvico

您可以通过将道具传递给 <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 被传递多个元素确实令人困惑,但 function signature 似乎只需要一个反应元素。我认为这个片段可以更容易理解。
这显然是文档中针对目标的最佳答案,但我也同意 manu 的观点,即可以编写它以更好地显示使用。更具体的问题代码如下所示:React.cloneElement(this.props.children, {myprop: "value"})React.cloneElement(this.props.children, {myprop: this.props.myprop}) 等。
这个答案获胜。在路由器为您做的事情中发生了什么也更清楚了。作为阅读代码的人,如果我知道 Comments 在 Index 中,我将查看 Index 以查看发送给 Comments 的道具。如果我碰巧知道 Comments 是一个路由器处理程序,我将查看路由器配置并找出 Index 父级评论,然后仍然去那里查看。
N
Nick

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

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

如果您需要传递孩子:

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


这很好,但它不会通过孩子,以防万一。
@zpr 我为 props.children 添加了一个示例
正如 @dgrcode answer 指出的那样,您应该使用 render 而不是 component
T
Taras Yaremkiv

反应路由器 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} />} />

PS 这仅适用于 alpha 版本,并在 v4 alpha 版本后被删除。在最新的 v4 中,再次使用路径和确切的道具。

react-lego 一个示例应用程序包含完全执行此操作的代码in routes.js on its react-router-4 branch


c
cgenco

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

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

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


R
Rafa Viotti

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

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

j
jul

您还可以使用 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 在全球凉亭构建中公开公开,所以我不这么认为。
这也允许使用 TransitionGroup 和 CSSTransitionGroup 设置动画过渡,而我无法使用 wrapper 方法进行工作。
奇怪的是官方文档中没有提到这一点。
React 文档不再推荐使用 Mixin:facebook.github.io/react/blog/2016/07/13/…
M
Meistro

您可以像这样通过 <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 但具有不同值的道具,这可能会出现问题。


此代码中 3 个句点/点的作用或含义:{...props}
我猜 Flux 可以避免将状态从父 App 发送到路由。我得到了上面的代码,但它并不明确,所以隐藏的魔法很丑陋,不容易追踪和跟踪正在发生的事情。
Spread operator explanation。这不是明确的,但这并不是最糟糕的事情,因为您传递的是不可变的道具。
这对我有用,但正确的语法是 {...this.props}
A
Andrew Khmylov

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

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

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

C
Chris

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 中命名新的位置感知组件,以便我可以正常使用它。除了额外的 import 行和 assignment 行之外,组件的行为与预期一样,并正常接收其所有 props,并添加了所有 route props。因此,我可以愉快地使用 this.props.history.push() 从组件生命周期方法重定向,检查位置等。

希望这可以帮助!


S
Subham Tripathi

我已经回答了这个问题here

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

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

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

同样,您可以使用 v5 中的 children 道具。

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

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

旁注 - 引用 React 路由器 children-func doc

有时您需要渲染路径是否与位置匹配。在这些情况下,您可以使用功能 children 道具。它的工作原理与 render 完全一样,只是无论是否匹配都会被调用。

<Route path="/" render={() => <Home name="Sai" />} />

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


i
icc97

您还可以结合 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
      }
    ]
}

我不确定这是如何工作的——但它看起来不对。您在函数中使用 this.props ,我很确定这不起作用。如果您使用纯函数而不是扩展 React.Component,那么您必须传入 props 作为参数,请参阅 Components and Props 上的 React 文档
h
holyxiaoxin

对于反应路由器 2.x。

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

在你的路线中......

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

确保第三个参数是一个对象,如:{ checked: false }


g
graham mendick

React Router 的问题在于它会渲染你的组件,因此会阻止你传入 props。另一方面,Navigation router 允许您呈现自己的组件。这意味着您不必像下面的代码和随附的 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();

M
Michael Hobbs

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

b
brc-dd

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

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

现在,如果您想访问组件内的 Route Props,则可以参考此 solution。在功能组件的情况下,还有另一个钩子 useParams() 在那篇文章中没有提到。

更多参考:React Router v5.1


⚠ 注意:您将无法访问 <Route props />。我们唯一可以同时拥有自定义道具和路由道具的是使用 <Route render/> - 像这样渲染道具:<Route path="/comments" render={(props) => <Comments myProp="value" {...props}/>}/>
@Mr.spShuvo 你错了。我已经更新了我的答案。检查出。对于类组件,您只需要 withRouter,对于功能组件,您只需要 hooks
好吧,可能是我部分错了。但是对于类组件,您不能使用钩子。感谢您的更新和更多参考链接。
m
min may

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

F
François Zaninotto

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


S
Snivio

这可能是使用带有 cookie 处理程序的 react-router-dom 的最佳方式

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

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

使用下面的解决方案,这适用于 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>

K
Kai

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

例如:

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

并且,您在没有道具的路由器的 component 中使用 MyComments

然后,您可以使用 this.myPropcomponentDidMount() 函数中获取“myValue”;