Comment passer des accessoires à {ceci.accessoires de jeu.enfant}
J'essaie de trouver la bonne façon de définir certains composants qui pourraient être utilisés de manière générique:
<Parent>
<Child value="1">
<Child value="2">
</Parent>
Il y a une logique pour le rendu entre les composants parents et enfants bien sûr, vous pouvez imaginer <select>
et <option>
comme exemple de cette logique.
Ceci est une implémentation fictive aux fins de la question:
var Parent = React.createClass({
doSomething: function(value) {
},
render: function() {
return (<div>{this.props.children}</div>);
}
});
var Child = React.createClass({
onClick: function() {
this.props.doSomething(this.props.value); // doSomething is undefined
},
render: function() {
return (<div onClick={this.onClick}></div>);
}
});
La question Est chaque fois que vous utilisez {this.props.children}
pour définir un composant wrapper, Comment transmettez-vous une propriété à tous ses les enfants?
18 réponses
Vous pouvez utiliser Réagir.Children pour itérer sur les enfants, puis cloner chaque élément avec de nouveaux accessoires (fusionnés peu profonds) en utilisant React.cloneElement par exemple:
const Child = ({ doSomething, value }) => (
<div onClick={() => doSomething(value)}>Click Me</div>
);
class Parent extends React.PureComponent {
doSomething = (value) => {
console.log('doSomething called by child with value:', value);
}
render() {
const { children } = this.props;
const childrenWithProps = React.Children.map(children, child =>
React.cloneElement(child, { doSomething: this.doSomething }));
return <div>{childrenWithProps}</div>
}
};
ReactDOM.render(
<Parent>
<Child value="1" />
<Child value="2" />
</Parent>,
document.getElementById('container')
);
Violon: https://jsfiddle.net/2q294y43/2/
Pour une façon légèrement plus propre de le faire, essayez:
<div>
{React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })}
</div>
Note : cela ne fonctionnera que s'il y a un seul enfant et qu'il s'agit d'un élément React valide.
Essayez ceci
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
Cela a fonctionné pour moi en utilisant react-15.1.
Passer des accessoires pour diriger les enfants.
Voir toutes les autres réponses
Transmettre des données globales partagées via l'arborescence des composants via context
Contexte est conçu pour partager des données qui peuvent être considérées comme "globales" pour une arborescence de composants React, tels que l'utilisateur authentifié actuel, le thème ou la langue préférée. 1
avertissement: Ceci est une réponse mise à jour, la précédente utilisait l'ancienne API de contexte
Il est basé sur Consommateur / fournir Principe. Tout d'abord, créez votre contexte
const { Provider, Consumer } = React.createContext(defaultValue);
Ensuite, utilisez via
<Provider value={/* some value */}>
{children} /* potential consumers */
<Provider />
Et
<Consumer>
{value => /* render something based on the context value */}
</Consumer>
Tous les consommateurs qui sont des descendants d'un fournisseur seront restitués chaque fois que la valeur prop du fournisseur change. la propagation du fournisseur à ses consommateurs descendants n'est pas soumise à la méthode shouldComponentUpdate, de sorte que le consommateur est mis à jour même lorsqu'un composant ancêtre quitte la mise à jour. 1
Plein exemple, semi-pseudo code.
import React from 'react';
const { Provider, Consumer } = React.createContext({ color: 'white' });
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: { color: 'black' },
};
}
render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}
class Toolbar extends React.Component {
render() {
return (
<div>
<p> Consumer can be arbitrary levels deep </p>
<Consumer>
{value => <p> The toolbar will be in color {value.color} </p>}
</Consumer>
</div>
);
}
}
, Vous pouvez utiliser React.cloneElement
, il est préférable de savoir comment il fonctionne avant de vous commencer à l'utiliser dans votre application. Il est introduit dans React v0.13
, Lisez la suite pour plus d'informations, donc quelque chose avec ce travail pour vous:
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
Apportez donc les lignes de la documentation React pour que vous compreniez comment tout cela fonctionne et comment vous pouvez les utiliser:
Dans React V0. 13 RC2, nous allons introduire une nouvelle API, similaire à Réagir.addons.cloneWithProps, avec ceci signature:
React.cloneElement(element, props, ...children);
Contrairement à cloneWithProps, cette nouvelle fonction n'a pas de magie comportement intégré pour fusionner style et className pour la même raison nous n'avons pas cette fonctionnalité de transferPropsTo. Personne n'est sûr de ce qu' exactement la liste complète des choses magiques sont, ce qui le rend difficile de raisonner sur le code et difficile à réutiliser lorsque le style a une signature différente (par exemple dans le prochain React Native).
Réagir.cloneElement est presque équivalent à:
<element.type {...element.props} {...props}>{children}</element.type>
Cependant, contrairement à JSX et cloneWithProps, il préserve également les refs. Ce signifie que si vous obtenez un enfant avec une ref sur, vous ne serez pas accidentellement voler à partir de votre ancêtre. Vous obtiendrez la même référence attachée à votre nouvel élément.
Un modèle commun est de cartographier vos enfants et d'ajouter un nouvel accessoire. De nombreux problèmes ont été signalés à propos de cloneWithProps perdant la ref, ce qui rend plus difficile la raison de votre code. Maintenant, à la suite de la même le motif avec cloneElement fonctionnera comme prévu. Par exemple:
var newChildren = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { foo: true })
});
Remarque: Réagir.cloneElement (child, {ref: 'newRef'}) remplace le ref il n'est donc toujours pas possible pour deux parents d'avoir une ref à la même enfant, sauf si vous utilisez le rappel-réf.
C'était une fonctionnalité critique pour entrer dans React 0.13 puisque les accessoires sont maintenant immuable. Le chemin de mise à niveau consiste souvent à cloner l'élément, mais en ce faisant, vous risquez de perdre l'arbitre. Par conséquent, nous avons besoin d'un plus belle mise à niveau le chemin d'accès ici. Comme nous avons été la mise à niveau callsites à Facebook nous avons réalisé que nous avons besoin de cette méthode. Nous avons reçu les mêmes commentaires de la communauté. Par conséquent nous avons décidé de faire une autre RC avant la version finale à assurez-vous que nous obtenons cette dans.
Nous prévoyons de finalement déprécier React.addons.cloneWithProps. Nous ne sommes pas le faire encore, mais c'est une bonne occasion de commencer à réfléchir vos propres utilisations et envisager D'utiliser React.cloneElement à la place. Nous allons être assurez-vous de navire un communiqué avec des avis de dépréciation avant que nous en fait retirez-le afin qu'aucune action immédiate ne soit nécessaire.
Plus ici...
Avec la mise à jour Réagir 16.3 vous pouvez maintenant utiliser Réagir.createContext .
import * as React from 'react';
// React.createContext accepts a defaultValue as the first param
const { Provider: ParentProvider, Consumer: ChildConsumer } = React.createContext();
class Parent extends React.Component {
doSomething = (value) => {
// Do something here with value
};
render() {
return (
<ParentProvider value={{ onClick: this.doSomething }}>
{this.props.children}
</ParentProvider>
);
}
}
class Child extends React.Component {
render() {
const { value } = this.props;
return (
<ChildConsumer>
(({ doSomething }) => {
return <div onClick={() => doSomething(value)}>{value}</div>
})
</ChildConsumer>
);
}
}
// Example of using Parent and Child
import * as React from 'react';
class SomeComponent extends React.Component {
render() {
<Parent>
<Child value={1} />
<Child value={2} />
</Parent>
}
}
De Réagir.createContext brille où réagissent.cloneElement case n'a pas pu gérer les composants imbriqués
class SomeComponent extends React.Component {
render() {
<Parent>
<Child value={1} />
<SomeOtherComp><Child value={2} /></SomeOtherComp>
</Parent>
}
}
J'avais besoin de corriger la réponse acceptée ci-dessus pour la faire fonctionner en utilisant that au lieu de ce pointeur. CE dans la portée de la fonction map n'avait pasdoSomething fonction définie.
var Parent = React.createClass({
doSomething: function() {
console.log('doSomething!');
},
render: function() {
var that = this;
var childrenWithProps = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { doSomething: that.doSomething });
});
return <div>{childrenWithProps}</div>
}})
Update: ce correctif est pour ECMAScript 5, dans ES6 il n'y a pas besoin dans var that = this
Manière plus propre considérant un ou plusieurs enfants
<div>
{ React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
, Vous n'avez plus besoin de {this.props.children}
. Maintenant, vous pouvez envelopper votre enfant à l'aide de render
dans Route
et passer vos accessoires, comme d'habitude:
<BrowserRouter>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/posts">Posts</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<hr/>
<Route path="/" exact component={Home} />
<Route path="/posts" render={() => (
<Posts
value1={1}
value2={2}
data={this.state.data}
/>
)} />
<Route path="/about" component={About} />
</div>
</BrowserRouter>
Aucune des réponses n'aborde le problème d'avoir des enfants qui sont des composants pas React, tels que des chaînes de texte. Une solution de contournement pourrait être quelque chose comme ceci:
// Render method of Parent component
render(){
let props = {
setAlert : () => {alert("It works")}
};
let childrenWithProps = React.Children.map( this.props.children, function(child) {
if (React.isValidElement(child)){
return React.cloneElement(child, props);
}
return child;
});
return <div>{childrenWithProps}</div>
}
Si vous avez plusieurs enfants que vous voulez passer props, Vous pouvez le faire de cette façon, en utilisant le React.Enfant.carte:
render() {
let updatedChildren = React.Children.map(this.props.children,
(child) => {
return React.cloneElement(child, { newProp: newProp });
});
return (
<div>
{ updatedChildren }
</div>
);
}
Si votre composant n'a qu'un enfant, il n'y a pas besoin de mappage, vous pouvez simplement cloneElement tout de suite:
render() {
return (
<div>
{
React.cloneElement(this.props.children, {
newProp: newProp
})
}
</div>
);
}
Suite à la réponse @and_rest, voici comment je clone les enfants et ajoute une classe.
<div className="parent">
{React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
Parent.jsx:
import React from 'react';
const doSomething = value => {};
const Parent = props => (
<div>
{
!props || !props.children
? <div>Loading... (required at least one child)</div>
: !props.children.length
? <props.children.type {...props.children.props} doSomething={doSomething} {...props}>{props.children}</props.children.type>
: props.children.map((child, key) =>
React.cloneElement(child, {...props, key, doSomething}))
}
</div>
);
Enfant.jsx:
import React from 'react';
/* but better import doSomething right here,
or use some flux store (for example redux library) */
export default ({ doSomething, value }) => (
<div onClick={() => doSomething(value)}/>
);
Et principal.jsx:
import React from 'react';
import { render } from 'react-dom';
import Parent from './Parent';
import Child from './Child';
render(
<Parent>
<Child/>
<Child value='1'/>
<Child value='2'/>
</Parent>,
document.getElementById('...')
);
Voir l'exemple ici: https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview
, Selon la documentation de cloneElement()
React.cloneElement(
element,
[props],
[...children]
)
Cloner et renvoyer un nouvel élément React en utilisant l'élément comme point de départ point. L'élément résultant aura les accessoires de l'élément d'origine avec les nouveaux accessoires fusionnés dans shallowly. Les nouveaux enfants remplaceront enfants existants. clé et ref de l'élément d'origine sera préserver.
React.cloneElement()
est presque équivalent à:<element.type {...element.props} {...props}>{children}</element.type>
Cependant, il conserve également les références. Cela signifie que si vous obtenez une enfant avec un ref dessus, vous ne le volerez pas accidentellement à votre ancêtre. Vous obtiendrez la même référence attachée à votre nouvel élément.
Donc, cloneElement est ce que vous utiliseriez pour fournir des accessoires personnalisés aux enfants. Cependant, il peut y avoir plusieurs enfants dans le composant et vous devrez faire une boucle dessus. Quelles autres réponses suggèrent est pour vous de les cartographier en utilisant React.Children.map
. Cependant React.Children.map
contrairement à React.cloneElement
change les clés de l'élément ajoutant et extra .$
comme préfixe. Vérifiez cette question pour plus de détails: réagir.cloneElement à L'intérieur de réagir.Enfant.la carte provoque le changement des clés d'élément
Si vous souhaitez l'éviter, vous devriez plutôt opter pour la fonction forEach
comme
render() {
const newElements = [];
React.Children.forEach(this.props.children,
child => newElements.push(
React.cloneElement(
child,
{...this.props, ...customProps}
)
)
)
return (
<div>{newElements}</div>
)
}
Peut-être que vous pouvez également trouver utile cette fonctionnalité, bien que beaucoup de gens aient considéré cela comme un anti-modèle, il peut toujours être utilisé si vous savez ce que vous faites et concevez bien votre solution.
Est-ce ce que vous avez besoin?
var Parent = React.createClass({
doSomething: function(value) {
}
render: function() {
return <div>
<Child doSome={this.doSomething} />
</div>
}
})
var Child = React.createClass({
onClick:function() {
this.props.doSome(value); // doSomething is undefined
},
render: function() {
return <div onClick={this.onClick}></div>
}
})
La façon la plus élégante de le faire:
{React.cloneElement(this.props.children, this.props)}
Une raison de réagir.les enfants ne travaillaient pas pour moi. C'est ce qui a fonctionné pour moi.
, je voulais juste ajouter une classe à l'enfant. similaire à la modification d'un accessoire
var newChildren = this.props.children.map((child) => {
const className = "MenuTooltip-item " + child.props.className;
return React.cloneElement(child, { className });
});
return <div>{newChildren}</div>;
L'astuce ici est la réaction .cloneElement . Vous pouvez passer n'importe quel accessoire de la même manière