Question Comment passer les accessoires à {this.props.children}


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 en cours pour le rendu entre les composants parents et enfants bien sûr, vous pouvez imaginer <select> et <option> comme un exemple de cette logique.

Ceci est une implémentation fictive dans le but 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 quand vous utilisez {this.props.children} pour définir un composant wrapper, comment transmettez-vous une propriété à tous ses enfants?


560
2017-09-03 08:47


origine


Réponses:


Vous pouvez utiliser Réagir.Enfants itérer sur les enfants, puis cloner chaque élément avec de nouveaux accessoires (fusion superficielle) 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/


625
2017-09-03 09:17



Pour une manière un peu plus propre de le faire, essayez:

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

Remarque: cela ne fonctionnera que s'il y a un seul enfant, et c'est un élément React valide.


288
2018-01-30 13:20



Essaye ça

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

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


58
2018-06-06 14:42



Passer les accessoires pour diriger les enfants.

Voir toutes les autres réponses

Transmettez des données globales partagées via l'arborescence des composants via le contexte

Le contexte est conçu pour partager des données pouvant être considérées comme «globales» pour un arbre de composants React, tel que l'utilisateur authentifié, le thème ou la langue préférée. 1

Disclaimer: Ceci est une réponse mise à jour, la précédente utilisait l'ancienne API de contexte

Il est basé sur le principe Consommateur / Fournir. 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 rendus à chaque fois que la valeur du fournisseur changera. La propagation de Provider à ses descendants Consumers n'est pas soumise à la méthode shouldComponentUpdate, le consommateur est mis à jour même si un composant ancêtre quitte la mise à jour.   1

Exemple complet, code semi-pseudo.

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

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


39
2017-09-08 22:45



Vous pouvez utiliser React.cloneElement, il est préférable de savoir comment cela fonctionne avant de 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 de React pour que vous compreniez comment tout fonctionne et comment vous pouvez les utiliser:

Dans React v0.13 RC2, nous allons introduire une nouvelle API, similaire à   React.addons.cloneWithProps, avec cette signature:

React.cloneElement(element, props, ...children);

Contrairement à cloneWithProps, cette nouvelle fonction n'a aucune 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 quoi   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).

React.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 un ref dessus, vous ne serez pas accidentellement   le voler de votre ancêtre. Vous obtiendrez le même ref attaché à   votre nouvel élément.

Un modèle commun est de cartographier vos enfants et ajouter un nouvel accessoire.   De nombreux problèmes ont été signalés à propos de cloneWithProps perdant l'arbitre,   il est plus difficile de raisonner sur votre code. Maintenant, en suivant la même   pattern 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: React.cloneElement (child, {ref: 'newRef'}) NE remplace le   ref donc il n'est toujours pas possible pour deux parents d'avoir un ref à la   même enfant, sauf si vous utilisez callback-refs.

C'était une caractéristique 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 pourriez perdre l'arbitre. Par conséquent, nous avions besoin d'une mise à jour plus agréable   chemin ici. Comme nous améliorons les callites sur Facebook, nous avons réalisé que   nous avions 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 un autre RC avant la version finale à   assurez-vous que nous obtenons cela.

Nous prévoyons éventuellement d'abandonner React.addons.cloneWithProps. N'étaient pas   le faire encore, mais c'est une bonne occasion de commencer à penser à   vos propres utilisations et pensez à utiliser React.cloneElement à la place. Bien être   sûr d'expédier une version avec des avis de dépréciation avant que nous réellement   retirez-le donc aucune action immédiate n'est nécessaire.

plus ici...


15
2018-06-12 07:03



Avec la mise à jour à Réagir 16.3 vous pouvez maintenant utiliser React.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>
  }
}

React.createContext brille où React.cloneElement case ne peut pas gérer les composants imbriqués

class SomeComponent extends React.Component {

  render() {
    <Parent>
      <Child value={1} />
      <SomeOtherComp><Child value={2} /></SomeOtherComp>
    </Parent>
  }
}

10
2018-04-09 06:02



Je devais corriger la réponse acceptée ci-dessus pour le faire fonctionner en utilisant cette au lieu de ce aiguille. Ce dans le cadre de la fonction de carte n'a pas faire quelque chose 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>
}})

Mise à jour: ce correctif est pour ECMAScript 5, dans ES6 il n'y a pas besoin de var that = this


5
2017-12-29 20:05



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

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>

5
2017-11-24 09:24