react-router - melewatkan props ke komponen handler


315

Saya memiliki struktur berikut untuk aplikasi React.js saya menggunakan 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);
});

Saya ingin melewatkan beberapa properti ke dalam Commentskomponen.

(biasanya saya akan melakukan ini seperti <Comments myprop="value" />)

Apa cara termudah dan tepat untuk melakukannya dengan React Router?


Masalahnya di sini, dan dalam kasus serupa seperti itu, terutama dengan kerangka kerja atau lib yang ditulis dalam beberapa lang, kurangnya sarana kombinasi tertentu (MoC). Primitif tampaknya ok di Bereaksi mereka cukup bagus, mendefinisikan komponen dengan primitif, dalam elemen Bereaksi dan komponen, Menhub , yang tampaknya juga ok di Bereaksi. Tetapi sarana kombinasi tidak lengkap. Seseorang harus dapat meneruskan alat peraga ke suatu komponen sambil menggabungkan suatu komponen dengan yang lain, tidak masalah jika dengan menempatkan satu komponen di dalam komponen lain sebagai anak dari komponen itu atau meneruskan satu komponen sebagai alat peraga ke komponen lainnya.
Selçuk

Dengan beberapa sintaks seperti <ComponentA x={<ComponentB y={<ComponentC z={} />} />} /> ATAU <ComponentA x={ComponentB(ComponentC()) } /> Jika tidak, masalah kombinasi abstraksi ini akan muncul kembali dan akan membutuhkan beberapa solusi yang kurang optimal dan tidak langsung yang disebut workarounds seperti pembungkus, dll.
Selçuk

Jawaban:


152

MEMPERBARUI

Sejak rilis baru, dimungkinkan untuk melewatkan alat peraga secara langsung melalui Routekomponen, tanpa menggunakan Wrapper. Misalnya dengan menggunakan renderprop .

Komponen:

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

Pemakaian:

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

Contoh Codesandbox


VERSI LAMA

Cara pilihan saya adalah membungkus Commentskomponen dan meneruskan pembungkus sebagai pengendali rute.

Ini adalah contoh Anda dengan perubahan yang diterapkan:

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
Saya mengalami masalah yang sama, tetapi bukankah solusi ini cepat terungkap?
captDaylight

8
Setuju dengan captDaylight, itu menjadi verbose. Lebih suka cara yang lebih baik untuk menangani ini!
Mattias Hallström

6
@mattiashallstrom IMO, cara yang lebih baik di 1.0 adalah cukup menambahkan properti ke rute. Lihat jawaban Thomas E.
k00k

28
Anda dapat menambahkan sintaks komponen stateless (hanya lambda) di sana juga, itu cukup singkat<Route path="comments" component={() => (<Comments myProp="value" />)}/>
Ciantic

33
Saya tidak akan pernah setuju dengan membuat komponen tambahan hanya untuk melewati properti menjadi "cara yang disukai". Ini verbose, rumit, rawan kesalahan dan jelas-jelas salah dalam segala hal yang bisa dibayangkan. Mungkin HANYA cara bereaksi router memungkinkan, tetapi menyebutnya "lebih disukai" adalah peregangan. Disukai oleh siapa?
Szczepan Hołyszewski

260

Jika Anda lebih suka tidak menulis pembungkus, saya kira Anda bisa melakukan ini:

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
Ini jawaban yang benar. Di react-router 1.0, Anda bisa mendapatkan routeobjek polos di komponen Anda. Inilah jawaban dari masalah github: github.com/rackt/react-router/issues/615#issuecomment-100432086
ycdesu

4
Ini adalah jawaban sederhana yang saya cari. Teknik-teknik lain berfungsi, tetapi membutuhkan 10x jumlah kode. Bekerja dengan baik untuk v1.0.x. Satu-satunya downside saya dapat melihat adalah jika Anda berniat untuk menggunakan komponen yang sama baik dengan dan tanpa wadah router Tetapi bagi saya, semua komponen tingkat atas saya memetakan 1-ke-1 dengan rute.
killthrush

12
Terima kasih banyak! Jika ada yang bertanya-tanya, properti foo akan tersedia di komponen Anda sebagai: this.props.route.foo
k00k

8
Bisakah ini ditetapkan sebagai jawaban yang benar untuk menghindari kebingungan?
Archibald

2
Nggak! Lihat jawaban Rajesh Naroth untuk solusi sesungguhnya :)
Alex

114

Menyalin dari komentar oleh ciantic dalam tanggapan yang diterima:

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

Ini adalah solusi paling anggun menurut saya. Berhasil. Membantuku.


Ini pada dasarnya sama dengan jawaban pembungkus di atas, tetapi jauh lebih sedikit bertele-tele. Man, meskipun, sintaks itu menyebalkan. Coba lempar dalam_ref
EdH

11
Ini seperti pembungkus anonim, sehingga semua alat suntik yang disuntikkan (mis. Lokasi) tidak terjawab. Kita harus secara manual melewati alat peraga seperti component={(props) => (<Comments myProp="value" location={ props.location } />)}tetapi semuanya menjadi berantakan lagi
yuji

1
@ Jacob. Alasan kami tidak membuat ulang konfigurasi router sehingga tidak mungkin menjadi penalti kinerja.
Sebastien Lorber

9
Pada React-Router 4, jika Anda menyediakan fungsi inline Anda akan mendapatkan banyak remounting yang tidak diinginkan. Untuk rendering inline, gunakan render prop. Tautan ke dokumen
Daniel Reina

1
@yuji Agar tidak terlalu berantakan, orang dapat melakukannya: component={(props) => (<Comments {...props} myProp="value" />)}untuk menjaga alat suntik yang disuntikkan
apelsinapa

57

Ini adalah solusi dari Rajesh , tanpa komentar tidak nyaman dari yuji , dan diperbarui untuk React Router 4.

Kode akan seperti ini:

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

Perhatikan bahwa saya menggunakan renderbukan component. Alasannya adalah untuk menghindari remounting yang tidak diinginkan . Saya juga meneruskan propske metode itu, dan saya menggunakan alat peraga yang sama pada komponen Komentar dengan operator penyebaran objek (ES7 proposal).


Sangat hebat dengan menyelesaikan pemasangan yang tidak diinginkan juga! +1
GLindqvist

44

Hanya tindak lanjut dari jawaban ColCh. Sangat mudah untuk abstrak pembungkus komponen:

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

Saya belum menguji solusi ini sehingga umpan balik apa pun penting.

Penting untuk dicatat bahwa dengan metode ini, semua alat peraga yang dikirim melalui Router (seperti params) akan ditimpa / dihapus.


1
Bob, apakah kamu akrab dengan penutupan? stackoverflow.com/questions/111102/…
sigmus

3
Dan jika Anda memerlukan kueri dan params dari router juga, maka sesuatu seperti ini akan berfungsi: return React.createElement(Component, _.assign({}, this.props, props));(Yang ini menggunakan _.assign untuk menyusun objek gabungan ... metode lain tentu saja tersedia).
Malcolm Dwyer

2
Anda mungkin ingin melewati anak-anak juga var wrapComponent = function (Component, props) {return React.createClass ({render: function () {return React.createElement (Komponen, alat peraga, this.props.children);}}); };
Julio Rodrigues

1
Ini adalah Komponen Tingkat Tinggi bagi mereka yang tidak terbiasa dengan mereka facebook.github.io/react/docs/higher-order-components.html
icc97

1
NB Ini untuk React Router versi lama sekarang. V4 saat ini memiliki render, componentdan childrenmetode untuk Route. Perhatikan bahwa sebagaimana dijawab oleh @dgrcode , Anda harus menggunakan rendersebagai ganticomponent
icc97

31

Anda bisa melewatkan props dengan meneruskannya ke <RouteHandler>(di v0.13.x) atau komponen Route itu sendiri di v1.0;

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

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

(dari panduan peningkatan di https://github.com/rackt/react-router/releases/tag/v1.0.0 )

Semua penangan anak akan menerima set alat peraga yang sama - ini mungkin berguna atau tidak tergantung pada keadaan.


1
Benar-benar teka-teki untuk melihat React.cloneElementditeruskan beberapa elemen tetapi tanda tangan fungsi tampaknya hanya mengambil satu elemen reaksi. Saya pikir cuplikan ini dapat dibuat lebih mudah untuk dipahami.
manu

2
Ini jelas jawaban terbaik tepat sasaran dengan dokumen, tetapi saya juga setuju dengan manu bahwa itu bisa ditulis untuk menunjukkan penggunaan yang lebih baik. Kode lebih spesifik untuk pertanyaan akan terlihat seperti: React.cloneElement(this.props.children, {myprop: "value"})atau React.cloneElement(this.props.children, {myprop: this.props.myprop})dll.
juanitogan

Jawaban ini menang. Juga jauh lebih jelas apa yang terjadi di tengah-tengah hal-hal yang dilakukan Router untuk Anda. Sebagai seseorang yang membaca kode, jika saya tahu Komentar ada di dalam Indeks, saya akan melihat Index untuk melihat alat peraga yang dikirim ke Komentar. Jika saya mengetahui bahwa Komentar adalah pengendali router, saya akan melihat konfigurasi router dan mengetahui bahwa komentar orang tua Index dan masih pergi melihat ke sana.
mjohnsonengr

24

Menggunakan ES6 Anda bisa membuat pembungkus komponen inline:

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

Jika Anda harus melewati anak-anak:

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


Ini bagus tetapi tidak melewati anak-anak kalau-kalau ada.
zpr

@ zpr Saya telah menambahkan contoh untuk props.children
Nick

2
Seperti yang dijawab oleh @dgrcode , Anda harus menggunakan rendersebagai ganticomponent
icc97

23

React-router v4 alpha

sekarang ada cara baru, untuk melakukan ini, walaupun sangat mirip dengan metode sebelumnya.

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 Ini hanya berfungsi dalam versi alpha, dan telah dihapus setelah rilis alpha v4. Di v4 terbaru, sekali lagi, dengan path dan alat peraga yang tepat.

bereaksi-lego contoh aplikasi berisi kode yang melakukan hal ini tepat di route.js pada cabang react-router-4-nya


21

Inilah solusi terbersih yang saya buat (React Router v4):

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

MyComponentmasih memiliki props.matchdan props.location, dan telah props.foo === "lol".


13

Bungkus dengan komponen fungsi stateless:

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

12

Anda juga bisa menggunakan mixin RouteHandler untuk menghindari komponen pembungkus dan lebih mudah menurunkan status induk sebagai alat peraga:

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
Ini terbuka untuk umum pada versi global seperti ReactRouter.RouteHandlerMixin, jadi saya tidak berpikir begitu.
juli

Ini juga memungkinkan untuk animasi transisi menggunakan TransitionGroup dan CSSTransitionGroup yang saya tidak bisa mulai bekerja menggunakan metode wrapper.
juli

2
Sungguh aneh bahwa tidak disebutkan tentang hal itu di dokumen resmi.
Kosmetika

Mixin tidak lagi direkomendasikan oleh React docs: facebook.github.io/react/blog/2016/07/13/…
icc97

12

Anda dapat mengirimkan alat peraga melalui yang <RouterHandler/>seperti ini:

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

Kelemahan dari ini adalah Anda melewati alat peraga tanpa pandang bulu. Jadi Commentsmungkin pada akhirnya menerima alat peraga yang benar-benar ditujukan untuk komponen yang berbeda tergantung pada konfigurasi rute Anda. Ini bukan masalah besar karena propstidak dapat diubah, tetapi ini bisa menjadi masalah jika dua komponen berbeda mengharapkan prop bernama footetapi dengan nilai yang berbeda.


1
Apa yang dilakukan 3 titik / titik atau maksud dalam kode ini:{...props}
Giant Elk

2
Saya kira Flux akan menghindari keharusan mengirim status dari Aplikasi induk ke suatu rute. Saya mendapatkan kode di atas berfungsi, tetapi tidak eksplisit sehingga rahasia yang tersembunyi jelek, tidak mudah untuk melacak dan melacak apa yang terjadi.
Giant Elk

2
Sebarkan penjelasan operator . Ini tidak eksplisit tetapi, itu bukan hal terburuk karena Anda melewati alat peraga yang tidak berubah.
Meistro


ini bekerja untuk saya tetapi sintaks yang benar adalah {... this.props}
Menangkan

10

Dalam 1.0 dan 2.0 Anda dapat menggunakan createElementpenyangga Routeruntuk menentukan bagaimana tepatnya untuk membuat elemen target Anda. Sumber dokumentasi

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

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

6

Anda juga dapat menggabungkan fungsi es6 dan stateless untuk mendapatkan hasil yang jauh lebih bersih:

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

Saya tidak yakin persis bagaimana ini bekerja - tetapi terlihat salah. Anda menggunakan this.propsfungsi, yang saya yakin tidak akan berfungsi. Jika Anda menggunakan fungsi murni alih-alih memperluas, React.Componentmaka Anda harus propsmemasukkannya sebagai argumen, lihat Bereaksi dokumen tentang Komponen dan Alat Peraga
icc97

6

React Router v 4 solution

Saya menemukan pertanyaan ini sebelumnya hari ini, dan di sini adalah pola yang saya gunakan. Semoga ini bermanfaat bagi siapa saja yang mencari solusi yang lebih terkini.

Saya tidak yakin apakah ini solusi terbaik , tetapi ini adalah pola saya saat ini. Saya biasanya memiliki direktori Core di mana saya menyimpan komponen yang biasa saya gunakan dengan konfigurasi yang relevan (loader, modals, dll), dan saya menyertakan file seperti ini:

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

Kemudian, dalam file yang dimaksud, saya akan melakukan hal berikut:

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

Anda akan melihat saya mengimpor ekspor default komponen saya sebagai case camel sederhana, yang memungkinkan saya memberi nama komponen baru yang sadar lokasi di CamelCase sehingga saya dapat menggunakannya secara normal. Selain jalur impor tambahan dan jalur penugasan, komponen berperilaku seperti yang diharapkan dan menerima semua alat peraga secara normal, dengan penambahan semua alat peraga rute. Jadi, saya dengan senang hati dapat mengarahkan kembali dari metode siklus hidup komponen dengan this.props.history.push (), memeriksa lokasi, dll.

Semoga ini membantu!


4

Untuk router reaksi 2.x.

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

dan di rute Anda ...

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

pastikan param 3 adalah sebuah objek seperti: { checked: false }.


1

Masalahnya dengan React Router adalah ia merender komponen-komponen Anda sehingga Anda berhenti menggunakan props. The router Navigasi , di sisi lain, memungkinkan Anda membuat komponen sendiri. Itu berarti Anda tidak harus melompati rintangan apa pun untuk melewati alat peraga sebagai kode berikut dan menyertai acara 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

Gunakan komponen dengan atau tanpa router berdasarkan jawaban 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}/>
);

Atau Anda bisa melakukannya dengan cara ini:

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

0

untuk router reaksi 2.5.2, solusinya sangat mudah:

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

Menggunakan komponen rute kustom , ini dimungkinkan di 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>
);

Adapun <MyRoute>kode komponen, itu harus seperti:

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;

Untuk detail lebih lanjut tentang pendekatan komponen rute kustom, lihat posting blog saya pada subjek: http://marmelab.com/blog/2016/09/20/custom-react-router-component.html


0

ini mungkin cara terbaik untuk menggunakan react-router-dom dengan sebuah cookie handler

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

dan gunakan 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

Gunakan solusi seperti di atas dan ini berfungsi di v3.2.5.

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

atau

<Route path="/foo">
  <Content
    lang="foo"
    meta={{
      description: lang_foo.description
    }}
  />
</Route>
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.