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?

593
demandé sur Tahir Ahmed 2015-09-03 11:47:25

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/

663
répondu Dominic 2018-08-07 18:47:08

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.

301
répondu Andres F Garcia 2017-11-02 06:14:05

Essayez ceci

<div>{React.cloneElement(this.props.children, {...this.props})}</div>

Cela a fonctionné pour moi en utilisant react-15.1.

61
répondu 7puns 2016-06-06 17:59:51

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

1https://facebook.github.io/react/docs/context.html

39
répondu Lyubomir 2018-05-25 14:18:56

, 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...

16
répondu Alireza 2017-11-27 12:49:59

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>
  }
}
15
répondu Kenneth Truong 2018-07-21 10:59:37

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

5
répondu olenak 2016-11-04 18:33:46

Manière plus propre considérant un ou plusieurs enfants

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
5
répondu and_rest 2017-03-02 00:07:23

, 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>
4
répondu yeasayer 2018-07-02 08:40:07

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>

}
3
répondu poynting 2017-10-16 14:07:58

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>
    );
}
2
répondu Nesha Zoric 2018-03-01 15:12:18

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>
1
répondu sidonaldson 2017-02-02 11:18:58

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

1
répondu Maksim Kostromin 2017-04-17 13:52:35

, 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>
    )

}
1
répondu Shubham Khatri 2018-01-11 05:57:17

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.

Fonction en tant que Composants Enfants

1
répondu Alexandr Cherednichenko 2018-02-10 21:19:35

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>
  }
})
0
répondu amit_183 2015-09-03 09:02:02

La façon la plus élégante de le faire:

    {React.cloneElement(this.props.children, this.props)}
0
répondu nitte93user3232918 2016-07-06 06:31:16

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

0
répondu aWebDeveloper 2017-12-27 08:54:51