N'y a-t-il pas un moyen simple de passer le props
d'un enfant à son parent à l'aide d'événements, dans React.js?
var Child = React.createClass({
render: function() {
<a onClick={this.props.onClick}>Click me</a>
}
});
var Parent = React.createClass({
onClick: function(event) {
// event.component.props ?why is this not available?
},
render: function() {
<Child onClick={this.onClick} />
}
});
Je sais que vous pouvez utiliser des composants contrôlés pour transmettre la valeur d'une entrée, mais il serait agréable de passer l'ensemble du kit n 'kaboodle. Parfois, le composant enfant contient un ensemble d'informations que vous préférez ne pas rechercher.
Peut-être y a-t-il un moyen de lier le composant à l'événement?
Après avoir utilisé React pendant plus d'un an, et sous l'impulsion de la réponse de Sebastien Lorber, j'ai conclu que le fait de passer des composants enfants comme arguments à des fonctions chez les parents est pas en fait, le React manière, ce n'était pas non plus une bonne idée. J'ai changé la réponse.
Edit : voir les exemples de fin pour les exemples mis à jour de ES6.
Cette réponse traite simplement le cas de relation directe parent-enfant. Lorsque le parent et l’enfant ont potentiellement beaucoup d’intermédiaires, vérifiez ceci réponse .
Bien qu'elles fonctionnent toujours bien, il manque quelque chose de très important dans les autres réponses.
N'y a-t-il pas un moyen simple de transmettre les accessoires d'un enfant à son parent en utilisant des événements, dans React.js?
Le parent a déjà cet accessoire! : si l'enfant a un accessoire, c'est que son parent l'a fourni à l'enfant! Pourquoi voulez-vous que l'enfant repasse l'accessoire au parent, alors que le parent en a évidemment déjà un?
Enfant : ça n'a 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 un seul enfant : utilisation de la valeur transmise à 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);
}
});
Parent avec liste d'enfants : vous avez toujours tout ce dont vous avez besoin sur le parent et vous 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);
}
});
Il est également possible d'utiliser this.handleChildClick.bind(null,childIndex)
puis d'utiliser this.state.childrenData[childIndex]
Notez que nous lions avec un contexte null
car sinon React envoie un avertissement relatif à son système auto-reliure . Utiliser null signifie que vous ne voulez pas changer le contexte de la fonction. Voir aussi .
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 de refs : 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, celui-ci peut transmettre tous les nœuds dom directement dans le rappel.
Le composant a une interface (props) et le parent ne doit rien présumer du fonctionnement interne de l'enfant, y compris de sa structure DOM interne ou des noeuds DOM pour lesquels il déclare des références. Un parent utilisant le ref d'un enfant signifie que vous couplez étroitement les 2 composants.
Pour illustrer le problème, je prendrai cette citation sur le Shadow DOM , qui est utilisé dans les navigateurs pour rendre des éléments tels que 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 que l’on considère comme des détails d’implémentation, donc inaccessibles pour vous. Le navigateur peut toutefois traverser cette limite à sa guise. Avec cette limite en place, ils ont pu créer tous les éléments HTML en utilisant les mêmes technologies anciennes, en dehors des divs et des span, comme vous le feriez.
Le problème est que si vous laissez les détails d'implémentation de l'enfant s'infiltrer dans le parent, il devient 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 le client trop accéder, ce qui rend très difficile la mise à niveau du code sans casser la rétrocompatibilité.
Si Chrome avait implémenté sa barre de défilement en laissant le client accéder aux noeuds 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 casseraient plus facilement lorsque Chrome effectue sa mise à jour automatique après la refactorisation de la barre de défilement ... Au lieu de cela, ils ne donnent accès qu'à certaines choses sûres, comme la personnalisation de certaines parties de la barre de défilement avec CSS.
À propos d'utiliser autre chose
Passer tout le composant 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, rendant l'application beaucoup plus difficile à raison à propos de.
Edit: exemples ES6
Comme beaucoup de gens utilisent maintenant ES6, voici les mêmes exemples pour la 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 sous forme d'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 cela peut aussi ê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>
)
AVERTISSEMENT PERF (s’applique à ES5/ES6): si vous utilisez PureComponent
ou shouldComponentUpdate
, les implémentations ci-dessus ne seront pas optimisé par défaut parce que vous utilisez onClick={e => doSomething()}
, ou une liaison directe pendant la phase de rendu, car il créera une nouvelle fonction à chaque fois que le parent restituera. 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é à this
dans le constructeur de la classe) de sorte que PureComponent
optimisation peut entrer en jeu ou vous pouvez implémenter votre propre shouldComponentUpdate
et ignorer le rappel dans le contrôle de comparaison des accessoires.
Vous pouvez également utiliser la bibliothèque Recompose , qui fournit des composants d'ordre supérieur pour obtenir des optimisations plus fines:
// 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)
Mise à jour (01/09/15): Le PO a fait de cette question un peu une cible en mouvement. Il a été mis à jour à nouveau. Donc, je me sens responsable de mettre à jour ma réponse.
Oui, c'est possible.
Vous pouvez résoudre ce problème en mettant à jour la variable onClick
de Child en this.props.onClick.bind(null, this)
:
var Child = React.createClass({
render: function () {
return <a onClick={this.props.onClick.bind(null, this)}>Click me</a>;
}
});
Le gestionnaire d’événements de votre parent peut alors accéder au composant et à l’événement de la manière suivante:
onClick: function (component, event) {
// console.log(component, event);
},
Le parent connaît déjà props
de l’enfant.
Cela n’est pas clair dans l’exemple fourni car aucun accessoire n’est fourni. Cet exemple de code pourrait mieux prendre en charge la question posée:
var Child = React.createClass({
render: function () {
return <a onClick={this.props.onClick}> {this.props.text} </a>;
}
});
var Parent = React.createClass({
getInitialState: function () {
return { text: "Click here" };
},
onClick: function (event) {
// event.component.props ?why is this not available?
},
render: function() {
return <Child onClick={this.onClick} text={this.state.text} />;
}
});
Il devient beaucoup plus clair dans cet exemple que vous savez déjà quels sont les accessoires de Child.
S'il est vraiment question d'utiliser les accessoires d'un enfant, vous pouvez éviter toute relation avec Child.
JSX a un propagation des attributs API que j'utilise souvent sur des composants comme Child. Il prend tous les props
et les applique à un composant. Enfant ressemblerait à ceci:
var Child = React.createClass({
render: function () {
return <a {...this.props}> {this.props.text} </a>;
}
});
Vous permettant d'utiliser les valeurs directement dans le parent:
var Parent = React.createClass({
getInitialState: function () {
return { text: "Click here" };
},
onClick: function (text) {
alert(text);
},
render: function() {
return <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />;
}
});
Et aucune configuration supplémentaire n'est requise lorsque vous raccordez des composants enfants supplémentaires
var Parent = React.createClass({
getInitialState: function () {
return {
text: "Click here",
text2: "No, Click here",
};
},
onClick: function (text) {
alert(text);
},
render: function() {
return <div>
<Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />
<Child onClick={this.onClick.bind(null, this.state.text2)} text={this.state.text2} />
</div>;
}
});
Mais je soupçonne que ce n’est pas votre cas d’utilisation actuel. Alors, creusons plus loin…
Il est difficile de parler de la nature générique de l'exemple fourni. J'ai créé un composant qui démontre une utilisation pratique de la question ci-dessus, implémenté de manière très Reacty :
exemple de travail DTServiceCalculator
repo DTServiceCalculator
Ce composant est un calculateur de service simple. Vous lui fournissez une liste de services (avec noms et prix) et calculera le total des prix sélectionnés.
ServiceItem
est le composant enfant dans cet exemple. Il n’a pas beaucoup d’opinions sur le monde extérieur. Il nécessite quelques accessoires , dont l’une est une fonction à appeler lorsque vous cliquez dessus.
<div onClick={this.props.handleClick.bind(this.props.index)} />
Il ne fait qu'appeler le callback handleClick
fourni avec le index
[ source ] fourni.
DTServicesCalculator
le composant parent est cet exemple. C’est aussi un enfant. Regardons.
DTServiceCalculator
crée une liste de composants enfants (ServiceItem
s) et leur fournit les accessoires [ source ]. C’est le composant parent de ServiceItem
, mais c’est le composant enfant du composant qui lui transmet la liste. Il ne possède pas les données. Donc, il délègue à nouveau le traitement du composant à son composant parent source
<ServiceItem chosen={chosen} index={i} key={id} price={price} name={name} onSelect={this.props.handleServiceItem} />
handleServiceItem
capture l'index, transmis par l'enfant, et le fournit à son parent [ source ]
handleServiceClick (index) {
this.props.onSelect(index);
}
Le concept de "propriété" est important dans React. Je recommande de lire plus à ce sujet ici .
Dans l’exemple que j’ai montré, je continue à déléguer la gestion d’un événement dans l’arborescence des composants jusqu’à ce que nous obtenions le composant qui possède l’état.
Lorsque nous y arrivons enfin, nous gérons la sélection/désélection d’état comme suit: [ source ]:
handleSelect (index) {
let services = […this.state.services];
services[index].chosen = (services[index].chosen) ? false : true;
this.setState({ services: services });
}
Essayez de garder vos composants les plus extérieurs aussi opaques que possible. S'efforcer de s'assurer qu'ils ont très peu de préférences sur la manière dont un composant parent peut choisir de les implémenter.
Sachez à qui appartiennent les données que vous manipulez. Dans la plupart des cas, vous devrez déléguer la gestion des événements dans l'arborescence au composant qui possède cet état.
De plus: le modèle de flux est un bon moyen de réduire ce type de raccordement nécessaire dans les applications.
Il semble y avoir une réponse simple. Considère ceci:
var Child = React.createClass({
render: function() {
<a onClick={this.props.onClick.bind(null, this)}>Click me</a>
}
});
var Parent = React.createClass({
onClick: function(component, event) {
component.props // #=> {Object...}
},
render: function() {
<Child onClick={this.onClick} />
}
});
La touche appelle bind(null, this)
sur l'événement this.props.onClick
, transmis par le parent. Maintenant, la fonction onClick accepte les arguments component
, ET event
. Je pense que c'est le meilleur des mondes.
C'était une mauvaise idée: laisser les détails d'implémentation de l'enfant s'infiltrer dans le parent n'était jamais un bon chemin. Voir la réponse de Sébastien Lorber.
La question est de savoir comment passer l'argument du composant enfant au composant parent. Cet exemple est facile à utiliser et à tester:
//Child component
class Child extends React.Component {
render() {
var handleToUpdate = this.props.handleToUpdate;
return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div>
)
}
}
//Parent component
class Parent extends React.Component {
constructor(props) {
super(props);
var handleToUpdate = this.handleToUpdate.bind(this);
}
handleToUpdate(someArg){
alert('We pass argument from Child to Parent: \n' + someArg);
}
render() {
var handleToUpdate = this.handleToUpdate;
return (<div>
<Child handleToUpdate = {handleToUpdate.bind(this)} />
</div>)
}
}
if(document.querySelector("#demo")){
ReactDOM.render(
<Parent />,
document.querySelector("#demo")
);
}
En gros, vous utilisez des accessoires pour envoyer des informations à et de Child et Parent.
Ajoutant à toutes les merveilleuses réponses, laissez-moi vous donner un exemple simple qui explique comment passer des valeurs d’un composant enfant à un composant parent dans React
App.js
class App extends React.Component {
constructor(){
super();
this.handleFilterUpdate = this.handleFilterUpdate.bind(this);
this.state={name:'igi'}
}
handleFilterUpdate(filterValue) {
this.setState({
name: filterValue
});
}
render() {
return (
<div>
<Header change={this.handleFilterUpdate} name={this.state.name} />
<p>{this.state.name}</p>
</div>
);
}
}
Header.js
class Header extends React.Component {
constructor(){
super();
this.state={
names: 'jessy'
}
}
Change(event) {
// this.props.change(this.state.names);
this.props.change('jessy');
}
render() {
return (
<button onClick={this.Change.bind(this)}>click</button>
);
}
}
Main.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(<App />, document.getElementById('app'));
Ça y est, vous pouvez maintenant transmettre les valeurs de votre client au serveur.
Jetez un coup d'œil à la fonction Change dans Header.js
Change(event) {
// this.props.change(this.state.names);
this.props.change('jessy');
}
C’est ainsi que vous insérez des valeurs dans les accessoires du client au serveur.
Voici une simple implémentation ES6 en 3 étapes utilisant la liaison de fonction dans le constructeur parent. C’est la première façon dont le tutoriel de réaction officiel le recommande (il existe également une syntaxe des champs de classe publique qui n’est pas traitée ici). Vous pouvez trouver toutes ces informations ici https://reactjs.org/docs/handling-events.html
Relier les fonctions parent afin que les enfants puissent les appeler (et transmettre les données au parent!: D)
Fonction parent
handleFilterApply(filterVals){}
Constructeur Parent
this.handleFilterApply = this.handleFilterApply.bind(this);
Prop Passed to Child
onApplyClick = {this.handleFilterApply}
Appel événement enfant
onClick = {() => {props.onApplyClick(filterVals)}