Quand devrais-je utiliser React.cloneElement par rapport à this.props.children?

118

Je suis toujours un noob chez React et dans de nombreux exemples sur Internet, je vois cette variation dans le rendu des éléments enfants que je trouve déroutante. Normalement, je vois ceci:

class Users extends React.Component {
  render() {
    return (
      <div>
        <h2>Users</h2>
        {this.props.children}
      </div>
    )
  }
}

Mais ensuite je vois un exemple comme celui-ci:

<ReactCSSTransitionGroup
     component="div"
     transitionName="example"
     transitionEnterTimeout={500}
     transitionLeaveTimeout={500}
     >
     {React.cloneElement(this.props.children, {
       key: this.props.location.pathname
      })}
</ReactCSSTransitionGroup>

Maintenant, je comprends l'API, mais la documentation ne précise pas exactement quand je devrais l'utiliser.

Alors, que fait l'un et que l'autre ne peut pas? Quelqu'un pourrait-il m'expliquer cela avec de meilleurs exemples?

Amit Erandole
la source
3
youtube.com/watch?v=hEGg-3pIHlE ce type montre comment il utilise cloneElement. Regardez ceci pour quelques exemples
iurii

Réponses:

69

Éditer:

Regardez plutôt la réponse de Vennesa , qui est une meilleure explication.

Original:

Tout d'abord, l' React.cloneElementexemple ne fonctionne que si votre enfant est un seul élément React.

Car presque tout {this.props.children}est celui que vous voulez. Le clonage est utile dans certains scénarios plus avancés, où un parent envoie un élément et le composant enfant doit changer certains accessoires sur cet élément ou ajouter des choses comme ref pour accéder à l'élément DOM réel.

Dans l'exemple ci-dessus, le parent qui donne à l'enfant ne connaît pas la clé requise pour le composant, il crée donc une copie de l'élément qui lui est donné et ajoute une clé basée sur un identifiant unique dans l'objet. Pour plus d'informations sur ce que fait la clé: https://facebook.github.io/react/docs/multiple-components.html

Morten Olsen
la source
183

props.childrenne sont pas les enfants réels; C'est celui descriptordes enfants. Vous n'avez donc rien à changer; vous ne pouvez modifier aucun accessoire ni modifier aucune fonctionnalité; vous pouvez seulement read from it. Si vous devez apporter des modifications, vous devez créer en new elementsutilisant React.CloneElement.

https://egghead.io/lessons/react-use-react-cloneelement-to-extend-functionality-of-children-components

Un exemple:

fonction de rendu principale d'un composant telle que App.js:

render() {   
    return(
            <Paragraph>
              <Sentence>First</Sentence>
              <Sentence>Second</Sentence>
              <Sentence>Third</Sentence>
            </Paragraph>   
    ) 
}

disons maintenant que vous devez ajouter un onClickà chaque enfant de Paragraph; donc dans votre Paragraph.jsvous pouvez faire:

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

alors simplement vous pouvez faire ceci:

render() {   
  return(
        <Paragraph onClick={this.onClick}>
          <Sentence>First</Sentence>
          <Sentence>Second</Sentence>
          <Sentence>Third</Sentence>
        </Paragraph>   
   ) 
}

Remarque: la React.Children.mapfonction ne voit que les top leveléléments, elle ne voit aucune des choses que ces éléments rendent; ce qui signifie que vous fournissez les accessoires directs aux enfants (ici les <Sentence />éléments). Si vous avez besoin que les accessoires soient transmis plus loin, disons que vous aurez un élément à <div></div>l'intérieur de l'un des <Sentence />éléments qui souhaite utiliser l' onClickaccessoire, dans ce cas, vous pouvez utiliser le Context APIpour le faire. Faites du Paragraphfournisseur et des Sentenceéléments un consommateur.

Vennesa
la source
11
C'est une réponse claire avec un exemple du monde réel. Il a besoin de plus de votes positifs.
SeaWarrior404
1
D'accord avec @ SeaWarrior404 - Je suppose que l'OP cherchait à parcourir une collection plutôt qu'un seul enfant parce que son interface utilisateur a "Utilisateurs" / pluriel comme titre. Utiliser React.Children.mapen conjonction avec React.cloneElementcomme le souligne @vennesa, est très puissant
jakeforaker
23

En fait, React.cloneElementn'est pas strictement associé à this.props.children.

C'est utile chaque fois que vous avez besoin de cloner des éléments react ( PropTypes.element) pour ajouter / remplacer des accessoires, sans vouloir que le parent ait des connaissances sur ces composants internes (par exemple, attacher des gestionnaires d'événements ou attribuer des key/ refattributs).

Les éléments de réaction sont également immuables .

React.cloneElement (element, [props], [... children]) est presque équivalent à: <element.type {...element.props} {...props}>{children}</element.type>


Cependant, le childrenprop dans React est particulièrement utilisé pour le confinement (alias composition ), l'appariement avec l' React.ChildrenAPI et React.cloneElement, le composant qui utilise des props.children peut gérer plus de logique (par exemple, transitions d'état, événements, mesures DOM, etc.) en interne tout en cédant la partie de rendu à partout où il est utilisé, React Router <switch/>ou un composant composé <select/>sont de bons exemples.

Une dernière chose qui mérite d'être mentionnée est que les éléments de réaction ne sont pas limités aux accessoires.

function SplitPane(props) {
  return (
    <div className="SplitPane">
      <div className="SplitPane-left">
        {props.left}
      </div>
      <div className="SplitPane-right">
        {props.right}
      </div>
    </div>
  );
}

function App() {
  return (
    <SplitPane
      left={
        <Contacts />
      }
      right={
        <Chat />
      } />
  );
}

Ils peuvent être tout accessoires qui fait sens, la clé était de définir un bon contrat pour le composant, de sorte que les consommateurs peuvent être de celui - ci découplés des détails de mise en œuvre sous - jacentes, peu importe que ce soit à l' aide React.Children, React.cloneElementou même React.createContext.

Xlee
la source