react-router-小道具をハンドラーコンポーネントに渡します


315

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でこれを行う最も簡単で正しい方法は何ですか?


ここでの問題、およびそのような同様の場合、特に一部の言語で記述されたフレームワークまたはライブラリの場合、組み合わせ手段(MoC)の特定の欠如。Reactではプリミティブは問題ないようです。React要素とコンポーネントであるMoCで、プリミティブを使用してコンポーネントを定義します。これはReactでも問題ないようです。しかし、組み合わせの手段は不完全です。コンポーネントを別のコンポーネントに結合するときに、小道具をコンポーネント渡すことができなければなりません。あるコンポーネントを別のコンポーネント内にその子として配置するか、あるコンポーネントを別のコンポーネントに小道具として渡すかは関係ありません。
セルチュク2017

<ComponentA x={<ComponentB y={<ComponentC z={} />} />} /> ORの ような構文 を使用すると、抽象化の組み合わせの<ComponentA x={ComponentB(ComponentC()) } /> この問題が再発し、ラップなどの回避策と呼ばれる最適ではない間接的な解決策が必要になります。
セルチュク2017

回答:


152

更新

新しいリリース以降Route、ラッパーを使用せずにコンポーネントを介して直接小道具を渡すことが可能です。たとえば、prop を使用し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} />} />

コードサンドボックスの例


古いバージョン

私の好ましい方法は、 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);
});

58
同じ問題が発生していますが、この解決策はすぐに冗長になりませんか?
captDaylight 2015年

8
captDaylightに同意すると、冗長になります。これを処理するより良い方法を好むでしょう!
MattiasHallström2015年

6
@mattiashallstrom IMO、1.0でのより良い方法は、ルートにプロパティを追加することです。Thomas Eの回答を参照してください。
k00k 2016年

28
ステートレスコンポーネント構文(ラムダのみ)を追加することもできます。これはかなり短いものです<Route path="comments" component={() => (<Comments myProp="value" />)}/>
Ciantic

33
「好ましい方法」であるプロパティを渡すためだけに追加のコンポーネントを作成することには決して同意しません。これは冗長で、複雑で、エラーが発生しやすく、考えられるあらゆる方法で明らかに間違っています。これは、react routerが許可する唯一の方法かもしれませんが、それを「優先」と呼ぶのは一続きです。誰が好む?
SzczepanHołyszewski16年

260

ラッパーを作成したくない場合は、次のようにすることができます。

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

11
これが正解です。react-router 1.0ではroute、コンポーネントでプレーンオブジェクトを取得できます。これがgithubの問題の答えです:github.com/rackt/react-router/issues/615#issuecomment-100432086
ycdesu

4
これは私が探していた簡単な答えです。他の手法も機能しますが、必要なコード量は10倍です。v1.0.xで問題なく動作します。私が見ることができる唯一の欠点は、ルーターコンテナーの有無にかかわらず同じコンポーネントを使用する場合です。しかし、私にとって、すべての最上位コンポーネントは、ルートと1対1でマッピングされていました。
killthrush 2016年

12
まことにありがとうございます!誰かが疑問に思っている場合、fooプロパティは次のようにコンポーネントで使用できます:this.props.route.foo
k00k

8
これは混乱を避けるために正しい答えとして設定できますか?
Archibald

2
いや!実際の解決策については、Rajesh Narothの回答を参照してください:)
Alex

114

受け入れられた応答のcianticによるコメントからのコピー:

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

これは私の意見では最も優雅な解決策です。できます。助けて頂きました。


これは基本的に上記のラッパーの回答と同じですが、それほど簡潔ではありません。しかし、その構文はひどいです。に投げ入れてみてください_ref
EdH

11
これは匿名のラッパーのようなものなので、挿入されたすべての小道具(場所など)が失われます。次のように手動で小道具を渡す必要component={(props) => (<Comments myProp="value" location={ props.location } />)}がありますが、すべてが再び乱雑になります
yuji

1
@JacobThomasonは、react router configを再レンダリングしないため、パフォーマンスが低下することはほとんどありません。
Sebastien Lorber

9
React-Router 4以降、インライン関数を提供すると、多くの望ましくない再マウントが発生します。インラインレンダリングには、レンダープロップを使用します。ドキュメントへのリンク
Daniel Reina

1
@yuji乱雑になりすぎないようにするために:component={(props) => (<Comments {...props} myProp="value" />)}注入された小道具を維持する
apelsinapa 2017

57

これはRajishソリューションであり、yujiによる不便なコメントはありませんが、React Router 4用に更新されています。

コードは次のようになります。

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

render代わりに使用することに注意してくださいcomponent。その理由は、望ましくない再マウントを回避するためです。また、propsそのメソッドにを渡し、Commentsコンポーネントで同じプロパティをオブジェクトスプレッドオペレーター(ES7提案)と共に使用しています。


望ましくないマウントも解決して本当に素晴らしいです!+1
GLindqvist

44

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など)が上書き/削除されることに注意することが重要です。


1
ボブ、クロージャーに詳しいですか?stackoverflow.com/questions/111102/...
sigmus

3
ルーターからクエリとreturn React.createElement(Component, _.assign({}, this.props, props));パラメーターも必要な場合は、次のようなものが機能します(これは_.assignを使用して結合オブジェクトを作成します...もちろん他のメソッドも使用できます)。
Malcolm Dwyer

2
子供たちも合格させたいかもしれません。| var wrapComponent = function(Component、props){return React.createClass({render:function(){return React.createElement(Component、props、this.props.children);}}); };
Julio Rodrigues

1
これは、慣れていない人のための高次コンポーネントですfacebook.github.io/react/docs/higher-order-components.html
icc97

1
注:これは古いバージョンのReact Router向けです。現在v4がありrendercomponentchildrenする方法Route@dgrcodeの回答が示すようrenderに、代わりに使用する必要があることに注意してくださいcomponent
icc97

31

小道具は、<RouteHandler>(v0.13.xで)またはv1.0のルートコンポーネント自体に渡すことで渡すことができます。

// 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のアップグレードガイドから

すべての子ハンドラーは同じセットの小道具を受け取ります-これは状況によっては役立つ場合もあればそうでない場合もあります。


1
React.cloneElement複数の要素が渡されるのを見るのは本当に困惑しますが、関数のシグネチャは1つの反応要素しかとらないようです。このスニペットは理解しやすくなると思います。
manu

2
これは明らかにドキュメントのターゲットに対する最良の回答ですが、使用をよりよく示すように記述できることにも同意します。質問へのコードより具体的には次のようになりますReact.cloneElement(this.props.children, {myprop: "value"})React.cloneElement(this.props.children, {myprop: this.props.myprop})など
juanitogan

この答えが勝ちます。また、ルーターがあなたのために行うことの最中で何が起こっているかについても、はるかに明確です。誰かがコードを読んでいるときに、コメントがインデックス内にあることがわかっている場合は、インデックスを見て、どの小道具がコメントに送信されているかを確認します。Commentsがルーターハンドラーであることを偶然知った場合は、ルーターの構成を調べて、Index parentsのコメントを見つけて、そこに引き続き行きます。
mjohnsonengr 2016年

24

ES6を使用すると、コンポーネントラッパーをインラインにすることができます。

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

子供を渡す必要がある場合:

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


これはいいですが、子供がいる場合には子供を通過しません。
zpr 2016

@zpr props.childrenの例を追加しました
Nick

2
以下のよう@dgrcodeの答えは指摘し、あなたが使用する必要がありますrender代わりにcomponent
icc97

23

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

PSこれはアルファバージョンでのみ機能し、v4アルファリリース後に削除されました。最新のv4では、パスと正確な小道具が追加されています。

react-legoサンプルアプリには、react-router-4ブランチのroutes.jsでこれ正確に実行するコードが含まれています


21

これが私が思いついた最もクリーンなソリューションです(React Router v4):

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

MyComponentまだprops.matchととprops.locationがありますprops.foo === "lol"


13

ステートレス関数コンポーネントでラップします。

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

12

RouteHandlerミックスインを使用してラッパーコンポーネントを回避し、親の状態を小道具としてより簡単に渡すこともできます。

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

1
ReactRouter.RouteHandlerMixinとしてグローバルバウアービルドで公開されているので、私はそうは思いません。
2015年

これにより、ラッパーメソッドを使用して動作させることができなかったTransitionGroupおよびCSSTransitionGroupを使用して遷移をアニメーション化することもできます。
2015年

2
公式ドキュメントでそれについて言及がないのは奇妙です。
コスメティカ2015年

:ミックスインはもはやリアクトドキュメントで推奨されていないfacebook.github.io/react/blog/2016/07/13/...
icc97

12

次の<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不変であるため、それほど大きな問題ではありませんが、2つの異なるコンポーネントが名前fooが付けられているが値が異なるプロップを期待している場合、これは問題になる可能性があります。


1
このコードでの3つのピリオド/ドットの意味または意味:{...props}
Giant Elk

2
Fluxを使用すると、親アプリからルートに状態を送信する必要がなくなると思います。上記のコードは機能しましたが、明示的ではないため、隠れたmajicは醜く、何が起こっているのかを追跡および追跡するのは簡単ではありません。
ジャイアントエルク2015

2
オペレーターの説明を広げる。明示的ではありませんが、不変の小道具を渡しているので、最悪のことではありません。
Meistro 2015

:ここではスプレッドオペレータのReactJSドキュメントだfacebook.github.io/react/docs/jsx-spread.htmlともfacebook.github.io/react/docs/transferring-props.html
巨人エルク

これは私にとってはうまくいきましたが、正しい構文は{... this.props}です
Win

10

1.0および2.0では、createElementpropを使用Routerして、ターゲット要素を作成する方法を正確に指定できます。ドキュメントソース

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

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

6

また、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渡す必要がpropsあります。コンポーネントとプロップ
icc97

6

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()を使用してコンポーネントのライフサイクルメソッドから楽しくリダイレ​​クトしたり、場所を確認したりできます。

お役に立てれば!


4

反応ルータ2.xの場合。

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

そしてあなたのルートで...

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

3番目のパラメータが次のようなオブジェクトであることを確認してください{ checked: false }


1

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

1

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

0

反応ルーター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'})}
  )
}
...

0

カスタムルートコンポーネントを使用すると、これは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


0

これはおそらく、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}))
       }
      />
    )
}

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

0

上記のようなソリューションを使用すると、これは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>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.