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?
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);
});
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}/>
);
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é.
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).
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.
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.
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
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"
.
Enveloppez-le avec un composant de fonction sans état:
<Router>
<Route
path='/'
component={({children}) =>
<MyComponent myProp={'myVal'}>{children}</MyComponent/>
}/>
</Router>
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.
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);
});
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>
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
}
]
}
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 }
.
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!
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>
};
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();
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><MyRoute> 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
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'})}
)
}
...
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;
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}))
}
/>
)
}