Modifier : voir les exemples finaux pour les exemples mis à jour ES6.
Cette réponse traite simplement le cas de la relation directe parent-enfant. Lorsque le parent et l'enfant ont potentiellement beaucoup d'intermédiaires, vérifiez cette réponse .
D'autres solutions ne sont pas pertinentes
Bien qu'ils fonctionnent toujours bien, il manque quelque chose de très important à d'autres réponses.
N'y a-t-il pas un moyen simple de transmettre les accessoires d'un enfant à son parent à l'aide d'événements, dans React.js?
Le parent a déjà cet accessoire enfant! : si l'enfant a un accessoire, c'est parce que son parent a fourni cet accessoire à l'enfant! Pourquoi voulez-vous que l'enfant remette l'accessoire au parent, alors que le parent a évidemment déjà cet accessoire?
Meilleure mise en œuvre
Enfant : ça ne doit vraiment pas être plus compliqué que ça.
var Child = React.createClass({
render: function () {
return <button onClick={this.props.onClick}>{this.props.text}</button>;
},
});
Parent avec enfant unique : en utilisant la valeur qu'il transmet à l'enfant
var Parent = React.createClass({
getInitialState: function() {
return {childText: "Click me! (parent prop)"};
},
render: function () {
return (
<Child onClick={this.handleChildClick} text={this.state.childText}/>
);
},
handleChildClick: function(event) {
// You can access the prop you pass to the children
// because you already have it!
// Here you have it in state but it could also be
// in props, coming from another parent.
alert("The Child button text is: " + this.state.childText);
// You can also access the target of the click here
// if you want to do some magic stuff
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
Parent avec liste d'enfants : vous avez toujours tout ce dont vous avez besoin sur le parent et n'avez pas besoin de rendre l'enfant plus compliqué.
var Parent = React.createClass({
getInitialState: function() {
return {childrenData: [
{childText: "Click me 1!", childNumber: 1},
{childText: "Click me 2!", childNumber: 2}
]};
},
render: function () {
var children = this.state.childrenData.map(function(childData,childIndex) {
return <Child onClick={this.handleChildClick.bind(null,childData)} text={childData.childText}/>;
}.bind(this));
return <div>{children}</div>;
},
handleChildClick: function(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
Il est également possible d'utiliser this.handleChildClick.bind(null,childIndex)
puis d'utiliserthis.state.childrenData[childIndex]
Notez que nous lions avec un null
contexte car sinon React émet un avertissement lié à son système de liaison automatique. L'utilisation de null signifie que vous ne souhaitez pas modifier le contexte de la fonction. Voir également .
À propos de l'encapsulation et du couplage dans d'autres réponses
C'est pour moi une mauvaise idée en terme de couplage et d'encapsulation:
var Parent = React.createClass({
handleClick: function(childComponent) {
// using childComponent.props
// using childComponent.refs.button
// or anything else using childComponent
},
render: function() {
<Child onClick={this.handleClick} />
}
});
Utilisation d'accessoires : Comme je l'ai expliqué ci-dessus, vous avez déjà les accessoires dans le parent, il est donc inutile de passer tout le composant enfant pour accéder aux accessoires.
Utilisation des références : vous avez déjà la cible de clic dans l'événement, et dans la plupart des cas, cela suffit. De plus, vous auriez pu utiliser une référence directement sur l'enfant:
<Child ref="theChild" .../>
Et accédez au nœud DOM dans le parent avec
React.findDOMNode(this.refs.theChild)
Pour les cas plus avancés où vous souhaitez accéder à plusieurs références de l'enfant dans le parent, l'enfant peut passer tous les nœuds dom directement dans le rappel.
Le composant a une interface (accessoires) et le parent ne doit rien supposer du fonctionnement interne de l'enfant, y compris sa structure DOM interne ou les nœuds DOM pour lesquels il déclare des références. Un parent utilisant une référence d'un enfant signifie que vous couplez étroitement les 2 composants.
Pour illustrer le problème, je vais prendre cette citation sur le Shadow DOM , qui est utilisé dans les navigateurs pour rendre des choses comme des curseurs, des barres de défilement, des lecteurs vidéo ...:
Ils ont créé une frontière entre ce que vous, le développeur Web pouvez atteindre et ce qui est considéré comme des détails d'implémentation, donc inaccessible pour vous. Le navigateur peut cependant traverser cette frontière à volonté. Avec cette limite en place, ils ont pu créer tous les éléments HTML en utilisant les mêmes bonnes technologies Web, à partir des divs et des étendues comme vous le feriez.
Le problème est que si vous laissez les détails de l'implémentation enfant s'infiltrer dans le parent, il est très difficile de refactoriser l'enfant sans affecter le parent. Cela signifie qu'en tant qu'auteur de bibliothèque (ou en tant qu'éditeur de navigateur avec Shadow DOM), cela est très dangereux car vous laissez trop accès au client, ce qui rend très difficile la mise à niveau du code sans rompre la rétrocompatibilité.
Si Chrome avait implémenté sa barre de défilement permettant au client d'accéder aux nœuds dom internes de cette barre de défilement, cela signifie que le client peut avoir la possibilité de simplement casser cette barre de défilement, et que les applications se briseraient plus facilement lorsque Chrome effectuerait sa mise à jour automatique après la refactorisation du scrollbar ... Au lieu de cela, ils ne donnent accès qu'à certaines choses sûres comme la personnalisation de certaines parties de la scrollbar avec CSS.
À propos de l'utilisation d'autre chose
Passer le composant entier dans le rappel est dangereux et peut amener les développeurs novices à faire des choses très étranges comme appeler childComponent.setState(...)
ou childComponent.forceUpdate()
, ou lui assigner de nouvelles variables, à l'intérieur du parent, ce qui rend l'application beaucoup plus difficile à raisonner.
Edit: exemples ES6
Comme de nombreuses personnes utilisent maintenant ES6, voici les mêmes exemples de syntaxe ES6
L'enfant peut être très simple:
const Child = ({
onClick,
text
}) => (
<button onClick={onClick}>
{text}
</button>
)
Le parent peut être soit une classe (et il peut éventuellement gérer l'état lui-même, mais je le passe comme accessoire ici:
class Parent1 extends React.Component {
handleChildClick(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
render() {
return (
<div>
{this.props.childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => this.handleChildClick(child,e)}
/>
))}
</div>
);
}
}
Mais il peut également être simplifié s'il n'a pas besoin de gérer l'état:
const Parent2 = ({childrenData}) => (
<div>
{childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => {
alert("The Child button data is: " + child.childText + " - " + child.childNumber);
alert("The Child HTML is: " + e.target.outerHTML);
}}
/>
))}
</div>
)
JsFiddle
AVERTISSEMENT PERF (s'applique à ES5 / ES6): si vous utilisez PureComponent
ou shouldComponentUpdate
, les implémentations ci-dessus ne seront pas optimisées par défaut car elles utilisent onClick={e => doSomething()}
ou se lient directement pendant la phase de rendu, car cela créera une nouvelle fonction à chaque rendu du parent. S'il s'agit d'un goulot d'étranglement dans votre application, vous pouvez transmettre les données aux enfants et les réinjecter dans un rappel "stable" (défini sur la classe parente et lié au this
constructeur de la classe) afin que l' PureComponent
optimisation puisse démarrer, ou vous peut implémenter le vôtreshouldComponentUpdate
et ignorer le rappel dans la vérification de comparaison des accessoires.
Vous pouvez également utiliser la bibliothèque Recompose , qui fournit des composants d'ordre supérieur pour réaliser des optimisations affinées:
// A component that is expensive to render
const ExpensiveComponent = ({ propA, propB }) => {...}
// Optimized version of same component, using shallow comparison of props
// Same effect as React's PureRenderMixin
const OptimizedComponent = pure(ExpensiveComponent)
// Even more optimized: only updates if specific prop keys have changed
const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)
Dans ce cas, vous pouvez optimiser le composant enfant en utilisant:
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)