reagire-router - passa i puntelli al componente gestore


315

Ho la seguente struttura per la mia applicazione React.js usando 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);
});

Voglio passare alcune proprietà nel Commentscomponente.

(normalmente lo farei così <Comments myprop="value" />)

Qual è il modo più semplice e giusto per farlo con React Router?


Il problema qui, e in casi simili, specialmente con i framework o le librerie scritte in alcuni lang, una certa mancanza di mezzi di combinazione (MoC). Le primitive sembrano ok in React, sono piuttosto buone, definendo componenti con primitive, in elementi React e il componente, MoC , che sembra ok anche in React. Ma i mezzi di combinazione sono incompleti. Uno deve essere in grado di passare gli oggetti di scena a un componente mentre combina un componente a un altro, non importa se inserendo un componente all'interno di un altro componente come figlio di esso o passando un componente come oggetti di scena a un altro.
Selçuk

Con alcune sintassi come <ComponentA x={<ComponentB y={<ComponentC z={} />} />} /> OR <ComponentA x={ComponentB(ComponentC()) } /> Altrimenti, questo problema di combinazioni di astrazioni si ripresenterà e avrà bisogno di soluzioni non ottimali e indirette chiamate soluzioni alternative come il wrapping ecc. Ecc. Le astrazioni devono essere cittadini di prima classe come primitivi, qualunque sia la percezione della prima classe.
Selçuk

Risposte:


152

AGGIORNARE

Dalla nuova versione, è possibile passare oggetti di scena direttamente tramite il Routecomponente, senza utilizzare un wrapper. Ad esempio, usando renderprop .

Componente:

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

Uso:

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

Esempio di Codesandbox


VECCHIA VERSIONE

Il mio modo preferito è avvolgere il Commentscomponente e passare il wrapper come gestore di route.

Questo è il tuo esempio con le modifiche applicate:

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
Sto riscontrando lo stesso problema, ma questa soluzione non è rapidamente prolissa?
captDaylight

8
Concordo con captDaylight, diventa dettagliato. Preferirei un modo migliore per gestirlo!
Mattias Hallström,

6
@mattiashallstrom IMO, il modo migliore in 1.0 è semplicemente aggiungere la proprietà al percorso. Vedi la risposta di Thomas E.
k00k,

28
potresti aggiungere anche una sintassi del componente senza stato (solo lambda) lì, è piuttosto breve<Route path="comments" component={() => (<Comments myProp="value" />)}/>
Ciantic,

33
Non concordo mai con la creazione di un componente aggiuntivo solo per passare le proprietà come "modo preferito". È prolisso, complicato, soggetto a errori e chiaramente sbagliato in ogni modo immaginabile. Potrebbe essere l'UNICO modo in cui il router di reazione lo consente, ma chiamarlo "preferito" è un allungamento. Preferito da chi?
Szczepan Hołyszewski,

260

Se preferisci non scrivere involucri, immagino che potresti farlo:

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
Questa è una risposta corretta Nel reagente-router 1.0, puoi ottenere routeun oggetto semplice nel tuo componente. Ecco la risposta al problema di github: github.com/rackt/react-router/issues/615#issuecomment-100432086
ycdesu,

4
Questa è la risposta semplice che stavo cercando. Le altre tecniche funzionano, ma richiedono 10 volte la quantità di codice. Funziona bene per v1.0.x. L'unico aspetto negativo che posso vedere è se si intende utilizzare lo stesso componente sia con che senza il contenitore del router. Ma per me, tutti i miei componenti di livello superiore sono stati mappati da 1 a 1 con route.
killthrush,

12
Grazie mille! Se qualcuno si sta chiedendo, la proprietà foo sarebbe disponibile nel tuo componente come: this.props.route.foo
k00k

8
Questa può essere impostata come una risposta corretta per evitare confusione?
Archibald,

2
No! Vedi la risposta di Rajesh Naroth per la vera soluzione :)
Alex

114

Copia dai commenti di ciantic nella risposta accettata:

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

Questa è la soluzione più aggraziata secondo me. Funziona. Mi ha aiutato.


Questo è fondamentalmente lo stesso della risposta wrapper sopra, ma è molto meno prolisso. Amico, però, quella sintassi fa schifo. Prova a lanciare un_ref
EdH il

11
È come un wrapper anonimo, quindi mancano tutti gli oggetti di scena iniettati (ad es. Posizione). Bisogna passare manualmente oggetti di scena come, component={(props) => (<Comments myProp="value" location={ props.location } />)}ma tutto diventa di nuovo disordinato
yuji,

1
@JacobThomason non eseguiamo nuovamente il rendering della configurazione del router di reazione, quindi è improbabile che si tratti di una penalità prestazionale.
Sebastien Lorber,

9
A partire da React-Router 4, se fornisci una funzione inline otterrai molti rimontaggi indesiderati. Per il rendering in linea, utilizzare il prop di rendering. Link alla documentazione
Daniel Reina,

1
@yuji Per non renderlo troppo disordinato si può fare: component={(props) => (<Comments {...props} myProp="value" />)}per mantenere i puntelli iniettati
apelsinapa

57

Questa è la soluzione di Rajesh , senza l'inconveniente commentato da yuji e aggiornata per React Router 4.

Il codice sarebbe così:

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

Si noti che io uso renderinvece di component. Il motivo è evitare rimontaggi indesiderati . Ho anche passato il propsmetodo a quel metodo e utilizzo gli stessi oggetti di scena sul componente Commenti con l'operatore diffusione oggetto (proposta ES7).


Davvero ottimo anche per risolvere montaggi indesiderati! +1
GLindqvist,

44

Solo un seguito alla risposta di ColCh. È abbastanza facile astrarre il wrapping di un componente:

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

Non ho ancora testato questa soluzione, quindi qualsiasi feedback è importante.

È importante notare che con questo metodo, tutti gli oggetti di scena inviati tramite il router (come i parametri) vengono sovrascritti / rimossi.


1
Bob, hai familiarità con le chiusure? stackoverflow.com/questions/111102/...
sigmus

3
E se hai bisogno anche della query e dei parametri dal router, funzionerà qualcosa del genere: return React.createElement(Component, _.assign({}, this.props, props));(Questo usa _.assign per comporre l'oggetto combinato ... ovviamente sono disponibili altri metodi).
Malcolm Dwyer,

2
Potresti voler passare anche i bambini var wrapComponent = function (Component, props) {return React.createClass ({render: function () {return React.createElement (Component, props, this.props.children);}}); };
Julio Rodrigues,

1
Questo è un componente di ordine superiore per coloro che non hanno familiarità con loro facebook.github.io/react/docs/higher-order-components.html
icc97

1
NB Questo è per una vecchia versione di React Router ora. V4 corrente ha render, componente childrenmetodi per la Route. Nota che, come sottolinea la risposta di @dgrcode , dovresti usare renderinvece dicomponent
icc97,

31

Puoi passare oggetti di scena passandoli a <RouteHandler>(in v0.13.x) o al componente Route stesso in v1.0;

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

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

(dalla guida all'aggiornamento su https://github.com/rackt/react-router/releases/tag/v1.0.0 )

Tutti i gestori di bambini riceveranno lo stesso set di oggetti di scena - questo può essere utile o no a seconda delle circostanze.


1
È davvero difficile vedere il React.cloneElementpassaggio di più elementi, ma la firma della funzione sembra richiedere un solo elemento di reazione. Penso che questo frammento possa essere reso più facile da capire.
manu,

2
Questa è chiaramente la migliore risposta sul target con i documenti, ma concordo anche con Manu che potrebbe essere scritto per mostrare un uso migliore. Il codice più specifico alla domanda sarebbe simile a: React.cloneElement(this.props.children, {myprop: "value"})oppure React.cloneElement(this.props.children, {myprop: this.props.myprop})ecc.
juanitogan,

Questa risposta vince. È anche molto più chiaro cosa sta succedendo nel mezzo delle cose che Router fa per te. Come qualcuno che legge il codice, se so che i commenti sono dentro Index, guarderò Index per vedere quali oggetti di scena vengono inviati ai commenti. Se mi capita di sapere che Comments è un gestore di router, guarderò la configurazione del router e scoprirò che i genitori di Index commentano e continuerò a cercare lì.
mjohnsonengr,

24

Utilizzando ES6 puoi semplicemente rendere in linea i wrapper dei componenti:

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

Se hai bisogno di passare bambini:

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


Questo è carino ma non passa attraverso i bambini nel caso ce ne siano.
zpr,

@zpr Ho aggiunto un esempio per props.children
Nick

2
Come sottolinea la risposta di @dgrcode , dovresti usare renderinvece dicomponent
icc97,

23

React-router v4 alfa

ora c'è un nuovo modo di farlo, anche se molto simile al metodo precedente.

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 Funziona solo in versione alpha e sono stati rimossi dopo la versione alpha di v4. Nella v4 più recente, è ancora una volta, con il percorso e puntelli esatti.

eagire-lego un'app di esempio contiene codice che fa esattamente questo in route.js sul suo ramo di reazione-router-4


21

Ecco la soluzione più pulita che ho trovato (React Router v4):

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

MyComponentha ancora props.matche props.location, e ha props.foo === "lol".


13

Avvolgilo con un componente di funzione stateless:

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

12

Puoi anche usare il mixin RouteHandler per evitare il componente wrapper e passare più facilmente lo stato del genitore come oggetti di scena:

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
È pubblicamente esposto sulla build del pergolato globale come ReactRouter.RouteHandlerMixin, quindi non credo.
lug

Ciò consente anche di animare le transizioni utilizzando TransitionGroup e CSSTransitionGroup che non sono riuscito a utilizzare con il metodo wrapper.
lug

2
È strano che non ci sia menzione di ciò nei documenti ufficiali.
Kosmetika,

I mixin non sono più raccomandati dai documenti di React: facebook.github.io/react/blog/2016/07/13/…
icc97

12

Puoi passare oggetti di scena <RouterHandler/>come questo:

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

L'aspetto negativo di questo è che stai passando oggetti di scena indiscriminatamente. Quindi Commentspotrebbe finire per ricevere oggetti di scena che sono realmente destinati a un componente diverso a seconda della configurazione dei percorsi. Non è un grosso problema poiché propsè immutabile, ma questo può essere problematico se due componenti diversi si aspettano un prop chiamato fooma con valori diversi.


1
Cosa significano o significano i 3 punti / punti in questo codice:{...props}
Giant Elk

2
Immagino che Flux eviterebbe la necessità di inviare lo stato dall'app principale a una route. Ho fatto funzionare il codice sopra, ma non è esplicito, quindi il Majic nascosto è brutto, non è facile rintracciare e tenere traccia di ciò che sta succedendo.
Giant Elk

2
Spiegare la spiegazione dell'operatore . Non è esplicito, ma non è la cosa peggiore dal momento che stai passando oggetti di scena che sono immutabili.
Meistro


questo ha funzionato per me, ma la sintassi corretta è {... this.props}
Vinci il

10

In 1.0 e 2.0 puoi usare createElementprop of Routerper specificare esattamente come creare il tuo elemento target. Fonte della documentazione

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

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

6

Puoi anche combinare le funzioni es6 e stateless per ottenere un risultato molto più pulito:

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

Non sono sicuro di come funzioni, ma sembra sbagliato. Stai usando this.propsuna funzione, che sono sicuro che non funzionerà. Se stai usando funzioni pure invece di estenderle, React.Componentdevi passare propscome argomento, vedi i documenti di React su Componenti e puntelli
icc97,

6

Soluzione React Router v 4

Oggi mi sono imbattuto in questa domanda, ed ecco il modello che uso. Speriamo che questo sia utile a chiunque cerchi una soluzione più attuale.

Non sono sicuro che questa sia la soluzione migliore , ma questo è il mio modello attuale per questo. In genere ho una directory Core in cui mantengo i miei componenti comunemente usati con le relative configurazioni (caricatori, modali, ecc.), E includo un file come questo:

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

Quindi, nel file in questione, farò quanto segue:

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

Noterai che ho importato l'esportazione predefinita del mio componente come umile custodia per cammello, che mi consente di nominare il nuovo componente sensibile alla posizione in CamelCase in modo da poterlo utilizzare normalmente. Oltre alla riga di importazione aggiuntiva e alla riga di assegnazione, il componente si comporta come previsto e riceve normalmente tutti gli oggetti di scena, con l'aggiunta di tutti gli oggetti di rotta. Pertanto, posso reindirizzare felicemente dai metodi del ciclo di vita dei componenti con this.props.history.push (), controllare la posizione, ecc.

Spero che questo ti aiuti!


4

Per reagire al router 2.x.

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

e nei tuoi percorsi ...

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

assicurarsi che il terzo parametro è un oggetto come: { checked: false }.


1

Il problema con il React Router è che rende i tuoi componenti e quindi ti impedisce di passare nei puntelli. Il router di navigazione , d'altra parte, ti consente di renderizzare i tuoi componenti. Ciò significa che non devi saltare attraverso i cerchi per passare oggetti di scena come il seguente codice e lo spettacolo JsFiddle di accompagnamento .

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

Utilizzare il componente con o senza router basato sulla risposta di 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}/>
);

O potresti farlo in questo modo:

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

0

per il reagente-router 2.5.2, la soluzione è così semplice:

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

Utilizzando un componente del percorso personalizzato , questo è possibile in 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>
);

Per quanto riguarda il <MyRoute>codice componente, dovrebbe essere qualcosa del tipo:

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;

Per maggiori dettagli sull'approccio del componente del percorso personalizzato, consulta il mio post sul blog sull'argomento: http://marmelab.com/blog/2016/09/20/custom-react-router-component.html


0

questo è probabilmente il modo migliore per utilizzare reagire-router-dom con un gestore di cookie

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

e usa un 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

Usa la soluzione come sopra e funziona nella v3.2.5.

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

o

<Route path="/foo">
  <Content
    lang="foo"
    meta={{
      description: lang_foo.description
    }}
  />
</Route>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.