那样做)使用 React Router 最简单和正确的方法是什么?" /> 那样做)使用 React Router 最简单和正确的方法是什么?"> 那样做)使用 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 最简单和正确的方法是什么?
<ComponentA x={<ComponentB y={<ComponentC z={} />} />} />
或 <ComponentA x={ComponentB(ComponentC()) } />
之类的语法 否则,这种抽象组合问题 将再次出现,并且需要一些不是最佳的间接解决方案,称为变通方法,如包装等。必须首先使用抽象阶级公民是原始人,无论第一阶级的感知是什么意思。
如果您不想编写包装器,我想您可以这样做:
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}/>
);
更新
从新版本开始,可以直接通过 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} />} />
旧版
我首选的方法是包装 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);
});
<Route path="comments" component={() => (<Comments myProp="value" />)}/>
从接受的回复中 ciantic 的评论中复制:
<Route path="comments" component={() => (<Comments myProp="value" />)}/>
这是我认为最优雅的解决方案。有用。帮助过我。
_ref
component={(props) => (<Comments myProp="value" location={ props.location } />)}
这样的道具,但一切又变得一团糟
component={(props) => (<Comments {...props} myProp="value" />)}
来维护注入的道具
这是 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 提议)。
只是对 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})}/>
我还没有测试过这个解决方案,所以任何反馈都很重要。
请务必注意,使用此方法,通过路由器发送的任何道具(例如参数)都会被覆盖/删除。
return React.createElement(Component, _.assign({}, this.props, props));
(这个使用 _.assign 来组成组合对象......当然还有其他方法)。
Route
的 render
、component
和 children
方法。请注意,正如 @dgrcode answer 指出的那样,您应该使用 render
而不是 component
您可以通过将道具传递给 <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 似乎只需要一个反应元素。我认为这个片段可以更容易理解。
React.cloneElement(this.props.children, {myprop: "value"})
或 React.cloneElement(this.props.children, {myprop: this.props.myprop})
等。
使用 ES6,您可以将组件包装器内联:
<Route path="/" component={() => <App myProp={someValue}/>} >
如果您需要传递孩子:
<Route path="/" component={(props) => <App myProp={someValue}>{props.children}</App>} >
反应路由器 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
这是我想出的最干净的解决方案(React Router v4):
<Route
path="/"
component={props => <MyComponent {...props} foo="lol" />}
/>
MyComponent
仍然有 props.match
和 props.location
,并且有 props.foo === "lol"
。
用无状态函数组件包装它:
<Router>
<Route
path='/'
component={({children}) =>
<MyComponent myProp={'myVal'}>{children}</MyComponent/>
}/>
</Router>
您还可以使用 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);
});
您可以像这样通过 <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
但具有不同值的道具,这可能会出现问题。
{...props}
在 1.0 和 2.0 中,您可以使用 Router
的 createElement
属性来指定如何精确地创建您的目标元素。 Documentation source
function createWithDefaultProps(Component, props) {
return <Component {...props} myprop="value" />;
}
// and then
<Router createElement={createWithDefaultProps}>
...
</Router>
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() 从组件生命周期方法重定向,检查位置等。
希望这可以帮助!
我已经回答了这个问题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/)
您还可以结合 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 文档
对于反应路由器 2.x。
const WrappedComponent = (Container, propsToPass, { children }) => <Container {...propsToPass}>{children}</Container>;
在你的路线中......
<Route path="/" component={WrappedComponent.bind(null, LayoutContainer, { someProp })}>
</Route>
确保第三个参数是一个对象,如:{ checked: false }
。
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();
根据 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 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}/>}/>
。
withRouter
,对于功能组件,您只需要 hooks。
对于 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'})}
)
}
...
使用自定义路由组件,这在 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><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;
有关自定义路由组件方法的更多详细信息,请查看我关于该主题的博文:http://marmelab.com/blog/2016/09/20/custom-react-router-component.html
这可能是使用带有 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}))
}
/>
)
}
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>
在 react-router-v3 中,我没有找到任何可行的解决方案,所以我做了一个很大的权衡,使用类继承而不是道具。
例如:
class MyComments extends Comments{
constructor(props) {
super(props);
this.myProp = myValue;
}
}
并且,您在没有道具的路由器的 component
中使用 MyComments
。
然后,您可以使用 this.myProp
在 componentDidMount()
函数中获取“myValue”;
route
普通对象。这是 github 问题的答案:github.com/rackt/react-router/issues/615#issuecomment-100432086