tepki yönlendirici - destekleri işleyici bileşenine geçir


315

React Router kullanarak React.js uygulamam için aşağıdaki yapıya sahibim :

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

Bazı özellikleri Commentsbileşene geçirmek istiyorum .

(normalde böyle yaparım <Comments myprop="value" />)

React Router ile bunu yapmanın en kolay ve doğru yolu nedir?


Buradaki sorun ve benzer durumlarda, özellikle bazı şeritlerde yazılmış çerçeveler veya kütüphanelerde, belirli bir kombinasyon aracı eksikliği (MOC). İlkeller , React'de iyi görünüyorlar, oldukça iyi durumdalar , ilkellerle bileşenleri, React öğelerinde ve React'de de iyi görünen MoC bileşeninde tanımlanıyorlar . Ancak kombinasyon araçları eksik. Bir bileşeni bir bileşeni diğeriyle birleştirirken desteği bir bileşene geçirebilmeli, bir bileşeni diğerinin alt öğesi olarak başka bir bileşenin içine yerleştirerek veya bir bileşeni bir sahne olarak diğerine geçirip geçirmemesi önemli değildir.
Selçuk

<ComponentA x={<ComponentB y={<ComponentC z={} />} />} /> VEYA gibi bir sözdizimi ile <ComponentA x={ComponentB(ComponentC()) } /> Aksi takdirde, soyutlama kombinasyonlarının bu sorunları tekrarlanır ve sarma vb. Gibi geçici çözümler adı verilen optimal ve dolaylı çözümlerden daha azına ihtiyaç duyacaktır.
Selçuk

Yanıtlar:


152

GÜNCELLEME

Yeni sürümden bu yana, sahne malzemesini Routebir Sarıcı kullanmadan doğrudan bileşen üzerinden geçirmek mümkündür . Örneğin, prop kullanarakrender .

Bileşen:

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

Kullanımı:

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

Codesandbox Örneği


ESKİ VERSİYON

Tercih ettiğim yol, Commentsbileşeni sarmak ve sargıyı bir rota işleyicisi olarak geçirmektir.

Değişikliklerin uygulandığı örnek:

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
Aynı sorunla karşılaşıyorum, ancak bu çözüm hızlı bir şekilde ayrıntılı hale gelmiyor mu?
captDaylight

8
CaptDaylight ile anlaşın, ayrıntılı olur. Bunu ele almak için daha iyi bir yol tercih ederdim!
Mattias Hallström

6
@mattiashallstrom IMO, 1.0'da daha iyi bir yol özelliği özelliği rotaya eklemektir. Thomas E'nin cevabına bakın.
k00k

28
Orada vatansız bir bileşen sözdizimi (sadece lambda) da ekleyebilirsiniz, oldukça kısa<Route path="comments" component={() => (<Comments myProp="value" />)}/>
Ciantic

33
Ben sadece "tercih edilen yol" olan özellikleri geçmek için ek bileşen oluşturma kabul asla. Ayrıntılı, karmaşık, hataya açık ve akla gelebilecek her şekilde açıkça yanlış. Yönlendiricinin izin verdiği SADECE yol olabilir, ancak "tercih edilen" olarak adlandırmak bir esnektir. Kim tarafından tercih edilir?
Szczepan Hołyszewski

260

Paketleyici yazmak istemiyorsanız, sanırım bunu yapabilirsiniz:

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
Bu doğru bir cevap. Reat-router 1.0'da, routebileşeninizde düz nesne elde edebilirsiniz. İşte github sorunun cevabı: github.com/rackt/react-router/issues/615#issuecomment-100432086
ycdesu

4
Aradığım basit cevap bu. Diğer teknikler çalışır, ancak kodun 10 katını gerektirir. V1.0.x için iyi çalışır. Görebildiğim tek dezavantajı, aynı bileşeni hem yönlendirici kabı ile hem de yönlendirici kabı olmadan kullanmayı düşünüyorsanız. Ama benim için, tüm üst düzey bileşenlerim 1'den 1'e rotalarla eşlendi.
killthrush

12
Çok teşekkürler! Merak eden biri varsa, foo özelliği bileşeninizde şu şekilde kullanılabilir: this.props.route.foo
k00k

8
Bu, karışıklığı önlemek için doğru bir cevap olarak ayarlanabilir mi?
Archibald

2
Hayır! Gerçek çözüm için Rajesh Naroth cevabına bakın :)
Alex

114

Kabul edilen yanıtta yorumlardan kiantik tarafından kopyalanması :

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

Bence bu en zarif çözüm. İşe yarıyor. Bana yardımcı oldu.


Bu temel olarak yukarıdaki sargı cevabı ile aynıdır, ancak çok daha az gariptir. Adam, bu sözdizimi berbat. _ref
EdH'yi

11
Anonim bir sargıya benziyor, bu nedenle enjekte edilen tüm aksesuarlar (örn. Konum) kaçırıldı. Biri manuel gibi sahne geçmek zorunda component={(props) => (<Comments myProp="value" location={ props.location } />)}ama hepsi tekrar dağınık olur
yuji

1
@JacobThomason, tepki yönlendirici yapılandırmasını yeniden oluşturmuyoruz, bu nedenle bir performans cezası olması olası değildir.
Sebastien Lorber

9
React-Router 4'ten itibaren, bir satır içi işlev sağlarsanız, çok fazla istenmeyen yeniden montaj elde edersiniz. Satır içi oluşturma için render prop'ı kullanın. Dokümanlara bağlantı
Daniel Reina

1
@yuji Çok dağınık yapmak için bir şey yapabilirsiniz: component={(props) => (<Comments {...props} myProp="value" />)}enjekte sahne korumak için
apelsinapa

57

Bu Yuji tarafından yorum ve rahatsız edici React Router 4 için güncellenen olmadan, Rajesh çözümdür .

Kod şöyle olurdu:

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

renderBunun yerine kullandığımı unutmayın component. Nedeni, istenmeyen yeniden montajdan kaçınmaktır . Ben de propsbu yönteme geçmek ve Yorumlar bileşen nesne yayma işleci (ES7 teklif) ile aynı sahne kullanıyorum.


İstenmeyen montajı da çözerek gerçekten harika! +1
GLindqvist

44

Sadece ColCh'in cevabını takip ediyorum. Bir bileşenin sargısını soyutlamak oldukça kolaydır:

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

Bu çözümü henüz test etmedim, bu nedenle herhangi bir geri bildirim önemlidir.

Bu yöntemle, Yönlendirici üzerinden gönderilen tüm aksesuarların (parametreler gibi) üzerine yazıldığını / kaldırıldığını not etmek önemlidir.


1
Bob, kapanışlara aşina mısın? stackoverflow.com/questions/111102/…
sigmus

3
Ve yönlendiriciden de sorgu ve parametrelere ihtiyacınız varsa, o zaman böyle bir şey işe yarayacaktır: return React.createElement(Component, _.assign({}, this.props, props));(Bu birleştirilmiş nesneyi oluşturmak için _.assign kullanır ... tabii ki başka yöntemler de mevcuttur).
Malcolm Dwyer

2
Çocukları da geçmek isteyebilirsiniz. var wrapComponent = function (Component, props) {return React.createClass ({render: function () {return React.createElement (Bileşen, sahne donanımı, bu.props.children);}}); };
Julio Rodrigues

1
Bu, onlara aşina olmayanlar için bir Üst Düzey
Bileşendir

1
Not: Bu şimdi React Router'ın eski bir sürümü için. Güncel v4 sahiptir render, componentve childrenyöntemleri Route. @Dgrcode cevabının işaret ettiği gibirendercomponent
icc97

31

<RouteHandler>Destekleri (v0.13.x dosyasında) veya Route bileşeninin kendisini v1.0'da ileterek geçebilirsiniz ;

// 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 adresindeki yükseltme kılavuzundan )

Tüm çocuk işleyicileri aynı sahne setini alacaktır - bu duruma bağlı olarak yararlı olabilir veya olmayabilir.


1
Çok React.cloneElementsayıda öğenin geçildiğini görmek gerçekten bulmaca ama işlev imzası sadece bir tepki öğesi alıyor gibi görünüyor. Sanırım bu pasajı daha kolay anlaşılabilir.
manu

2
Bu, dokümanlarla hedefe yönelik en iyi cevaptır, ancak kullanımı daha iyi göstermek için yazılabileceğini de kabul ediyorum. Soruya daha spesifik olan kod şöyle görünecektir: React.cloneElement(this.props.children, {myprop: "value"})or React.cloneElement(this.props.children, {myprop: this.props.myprop})etc.
juanitogan

Bu cevap kazanır. Ayrıca Router'ın sizin için yaptığı şeylerin ortasında neler olduğu çok daha açık. Kodu okuyan biri olarak, Yorumlar'ın Dizin içinde olduğunu biliyorsanız, Yorumlara hangi desteklerin gönderildiğini görmek için Dizin'e bakacağım. Yorumlar'ın bir yönlendirici işleyicisi olduğunu bilirsem, yönlendirici yapılandırmasına bakacağım ve Index ebeveynlerinin yorumlarını bulacağım ve yine de oraya bakacağım.
mjohnsonengr

24

ES6'yı kullanarak bileşen paketlerini satır içinde yapabilirsiniz:

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

Çocukları geçmeniz gerekiyorsa:

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


Bu güzel ama herhangi bir durumda çocuklardan geçmiyor.
zpr

@zpr props.children için bir örnek ekledim
Nick

2
@Dgrcode cevabının işaret ettiği gibi render,component
icc97

23

React-router v4 alfa

şimdi, önceki yönteme çok benzemesine rağmen, bunu yapmanın yeni bir yolu var.

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 Bu yalnızca alfa sürümünde çalışır ve v4 alfa sürümünden sonra kaldırılmıştır. En son v4'te, yol ve tam sahne ile bir kez daha.

tepki-lego örnek bir uygulama tam olarak tepki-router-4 dalında route.js bunu yapan kod içeriyor


21

İşte bulduğum en temiz çözüm (React Router v4):

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

MyComponenthala var props.matchve props.locationvar props.foo === "lol".


13

Durum bilgisi olmayan bir işlev bileşeniyle sarın:

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

12

Sarma bileşenini önlemek ve üst öğe durumunu sahne olarak daha kolay bir şekilde geçirmek için RouteHandler karışımını da kullanabilirsiniz:

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
Genel olarak ReactRouter.RouteHandlerMixin olarak küresel bower yapısına maruz kaldı, bu yüzden sanmıyorum.
Temmuz

Bu aynı zamanda sarma yöntemini kullanarak işe alamadım TransitionGroup ve CSSTransitionGroup kullanarak geçişler animasyon sağlar.
Temmuz

2
Resmi belgelerde bundan bahsedilmemesi garip.
Kosmetika

React dokümanları tarafından artık
mixinler önerilmiyor

12

Aşağıdaki <RouterHandler/>gibi sahne içinden geçebilirsiniz :

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

Bunun dezavantajı, gelişigüzel bir şekilde sahne geçiyorsunuz. Bu nedenle Comments, rota yapılandırmanıza bağlı olarak gerçekten farklı bir bileşen için tasarlanan aksesuarlar alınabilir. propsDeğişmez olduğu için çok büyük bir anlaşma değil , ancak iki farklı bileşen adlı fooancak farklı değerlere sahip bir destek bekliyorsa bu sorunlu olabilir .


1
Bu kodda 3 nokta / nokta ne yapar veya ne anlama gelir:{...props}
Giant Elk

2
Flux'un ana uygulamadan bir rotaya durum gönderme ihtiyacını ortadan kaldırdığını düşünüyorum. Yukarıdaki kod çalışma var, ama gizli majic çirkin, iz ve ne olup olmadığını izlemek kolay değil bu yüzden açık değil.
Dev Elk

2
Forma operatörü açıklaması . Açık değil ama, değişmez olan sahne geçtiğiniz için en kötü şey değil.
Meistro


Bu benim için çalıştı ancak doğru sözdizimi {... this.props}
Kazan

10

1.0 ve 2.0'da , hedef öğenizi tam olarak nasıl oluşturacağınızı belirtmek için createElementprop of komutunu kullanabilirsiniz Router. Dokümantasyon kaynağı

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

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

6

Daha temiz bir sonuç elde etmek için es6 ve durum bilgisi olmayan işlevleri de birleştirebilirsiniz :

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

Bunun nasıl çalıştığından tam olarak emin değilim - ama yanlış görünüyor. this.propsİşe yaramayacağından eminim bir işlev kullanıyorsunuz . React.Componentprops
Genişletmek

6

React Router v 4 çözümü

Bu soruya bugün daha önce rastladım ve işte kullandığım kalıp bu. Umarım bu daha güncel bir çözüm arayan herkes için yararlıdır.

Bunun en iyi çözüm olup olmadığından emin değilim , ama bu benim şu anki kalıbım. Ben genellikle ilgili yapılandırmaları (yükleyiciler, modals, vb) ile yaygın olarak kullanılan bileşenleri tutmak bir çekirdek dizini var ve ben böyle bir dosya dahil:

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

Ardından, söz konusu dosyada aşağıdakileri yapacağım:

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

Bileşenimin varsayılan dışa aktarmasını mütevazi deve kasası olarak içe aktardığımı fark edeceksiniz. Ek içe aktarma hattı ve atama hattı dışında, bileşen beklendiği gibi davranır ve tüm rota desteklerinin eklenmesiyle tüm desteklerini normal şekilde alır. Böylece, this.props.history.push () ile bileşen yaşam döngüsü yöntemlerinden mutlu bir şekilde yeniden yönlendirebilirim, konumu kontrol edebilirim, vb.

Bu yardımcı olur umarım!


4

Reaksiyon yönlendirici 2.x için.

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

ve rotalarınızda ...

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

emin olun 3 param gibi bir nesnedir: { checked: false }.


1

React Router ile ilgili sorun, bileşenlerinizi oluşturması ve böylece sahne içinden geçmenizi durdurmasıdır. Navigasyon yönlendirici , diğer taraftan, kendi bileşenlerini görüntülemeyi sağlar. Bu, aşağıdaki kod ve beraberindeki JsFiddle şovu gibi desteklerden geçmek için herhangi bir çembere atlamanız gerekmediği anlamına gelir .

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

Bileşeni Rajesh Naroth yanıtına göre yönlendiricili veya yönlendiricisiz kullanın.

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

Veya bunu şu şekilde yapabilirsiniz:

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

0

tepki yönlendiricisi 2.5.2 için çözüm çok kolaydır:

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

Özel bir rota bileşeni kullanarak , React Router v3'te bu mümkündür.

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

Gelince <MyRoute>bileşen kod, bir şeyler gibi olmalıdır:

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;

Özel rota bileşeni yaklaşımı hakkında daha fazla bilgi için, konu hakkındaki blog yayınımı kontrol edin: http://marmelab.com/blog/2016/09/20/custom-react-router-component.html


0

bu büyük olasılıkla bir çerez işleyici ile tepki-yönlendirici-dom kullanmanın en iyi yoludur

index.js içinde

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

ve bir çerez kullanın

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

Yukarıdaki gibi bir çözüm kullanın ve bu v3.2.5'te çalışır.

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

veya

<Route path="/foo">
  <Content
    lang="foo"
    meta={{
      description: lang_foo.description
    }}
  />
</Route>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.