web-dev-qa-db-fra.com

react-router - transmet les accessoires au composant de gestion

J'ai la structure suivante pour mon application React.js avec 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);
});

Je souhaite transmettre des propriétés au composant Comments

(normalement, je ferais ceci comme <Comments myprop="value" />)

Quelle est la manière la plus simple et la plus appropriée de le faire avec React Router?

278
Kosmetika

UPDATE depuis la nouvelle version, il est possible de passer des accessoires directement via le composant Route, sans utiliser de wrapper 

Par exemple, en utilisant render prop. Lien pour réagir routeur: https://reacttraining.com/react-router/web/api/Route/render-func

Exemple de code sur codesandbox: https://codesandbox.io/s/z3ovqpmp44

Composant

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

Et utilisation

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

ANCIENNE VERSION

Ma méthode préférée est d'encapsuler le composant Comments et de transmettre l'encapsuleur en tant que gestionnaire d'itinéraire.

Voici votre exemple avec les modifications appliquées:

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

Si vous préférez ne pas écrire de wrappers, je suppose que vous pourriez faire ceci:

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

Copie des commentaires de ciantic dans la réponse acceptée: 

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

C’est la solution la plus gracieuse à mon avis. Ça marche. M'a aidé.

111
Rajesh Naroth

C’est la solution de Rajesh , sans l’inconvénient/ commenté par yuji et mis à jour pour React Router 4.

Le code serait comme ça:

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

Notez que j'utilise render au lieu de component. La raison est d'éviter un remontage indésirable . Je passe également la props à cette méthode et j'utilise les mêmes accessoires sur le composant Comments avec l'opérateur d'étalement d'objet (proposition ES7).

50
Daniel Reina

Juste un suivi de la réponse de ColCh. Il est assez facile d’abstraire le wrapping d’un composant:

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

Je n'ai pas encore testé cette solution, donc tout retour d'information est important.

Il est important de noter qu'avec cette méthode, tous les accessoires envoyés via le routeur (tels que les paramètres) sont écrasés/supprimés.

43
sigmus

Vous pouvez transmettre des accessoires en les transmettant à <RouteHandler> (dans la version 0.13.x) ou au composant Route lui-même dans la version 1.0;

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

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

(à partir du guide de mise à niveau à https://github.com/rackt/react-router/releases/tag/v1.0.0 )

Tous les enfants manipulateurs recevront le même ensemble d'accessoires - cela peut être utile ou non, selon les circonstances.

30
cachvico

React-router v4 alpha

maintenant, il existe une nouvelle façon de procéder, bien que très similaire à la méthode précédente.

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

P.S. Cela ne fonctionne que dans la version alpha et a été supprimé après la publication de la version v4 alpha. Dans la dernière v4, est à nouveau, avec le chemin et les accessoires exacts.

react-lego un exemple d'application contient du code qui fait exactement ceci dans routes.js sur sa branche react-router-4

22
peter.mouland

Voici la solution la plus propre que j'ai proposée (React Router v4):

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

MyComponent a toujours props.match et props.location et a props.foo === "lol".

19
cgenco

Enveloppez-le avec un composant de fonction sans état:

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

Vous pouvez passer des accessoires via le <RouterHandler/> comme ceci:

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'inconvénient est que vous transmettez des accessoires sans discernement. Donc, Comments peut finir par recevoir des accessoires vraiment destinés à un composant différent en fonction de la configuration de vos itinéraires. Ce n'est pas énorme, car props est immuable, mais cela peut être problématique si deux composants différents attendent un accessoire nommé foo mais avec des valeurs différentes.

11
Meistro

Vous pouvez également utiliser le mélange RouteHandler pour éviter le composant enveloppe et transmettre plus facilement l'état du parent en tant qu'accessoires:

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

Dans les versions 1.0 et 2.0, vous pouvez utiliser createElement prop de Router pour spécifier comment créer exactement votre élément cible. Source de la documentation

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

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

Vous pouvez également combiner les fonctions es6 et stateless pour obtenir un résultat beaucoup plus net:

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
      }
    ]
}
5
Zhiwei Huang

Pour réagir routeur 2.x.

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

et dans vos itinéraires ...

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

assurez-vous que le 3ème paramètre est un objet tel que: { checked: false }.

4
holyxiaoxin

Solution React Router v 4

Je suis tombé par hasard sur cette question plus tôt dans la journée et voici le schéma que j’utilise. J'espère que cela sera utile à quiconque recherchant une solution plus actuelle.

Je ne sais pas si c'est la solution best, mais c'est ce que je fais actuellement. J'ai généralement un répertoire Core où je conserve mes composants couramment utilisés avec leurs configurations appropriées (chargeurs, modaux, etc.), et j'inclus un fichier comme celui-ci:

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

Ensuite, dans le fichier en question, je ferai ce qui suit: 

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

Vous remarquerez que j’importe l’exportation par défaut de mon composant sous forme d’humble cas, ce qui me permet de nommer le nouveau composant lié à la localisation dans CamelCase afin que je puisse l’utiliser normalement. Hormis la ligne d'importation supplémentaire et la ligne d'affectation, le composant se comporte comme prévu et reçoit normalement tous ses accessoires, avec l'ajout de tous les accessoires de route. Ainsi, je peux facilement rediriger des méthodes de cycle de vie de composant avec this.props.history.Push (), vérifier l’emplacement, etc.

J'espère que cela t'aides!

4
Chris

Utilisez le composant avec ou sans routeur basé sur la réponse de 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}/>
);

Ou vous pouvez le faire de cette façon:

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

Le problème avec le routeur React est qu’il restitue vos composants et vous empêche de transmettre des accessoires. Le routeur Navigation , quant à lui, vous permet de restituer vos propres composants. Cela signifie que vous n'avez pas à sauter dans les cerceaux pour passer dans les accessoires tels que le code suivant et le jeu d'accompagnement 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
graham mendick

En utilisant un composant custom route, cela est possible dans 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>
);

En ce qui concerne le code de composant <MyRoute>, il devrait ressembler à ceci:

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;

Pour plus de détails sur l'approche par composant de route personnalisée, consultez mon article de blog sur le sujet: http://marmelab.com/blog/2016/09/20/custom-react-router-component.html

0
François Zaninotto

pour react-router 2.5.2, la solution est si simple:

    //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
min may
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
nik.ss

c'est probablement la meilleure façon d'utiliser react-router-dom avec un gestionnaire de cookies 

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

et utiliser 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
Snivio