Quando devo usar React.cloneElement vs this.props.children?

118

Ainda sou um novato no React e em muitos exemplos na internet, vejo essa variação na renderização de elementos filhos que considero confusa. Normalmente eu vejo isso:

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

Mas então vejo um exemplo como este:

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

Agora eu entendo a API, mas os documentos não deixam exatamente claro quando devo usá-la.

Então, o que um faz que o outro não pode? Alguém poderia me explicar isso com melhores exemplos?

Amit Erandole
fonte
3
youtube.com/watch?v=hEGg-3pIHlE esse cara está mostrando como ele usa cloneElement. Veja alguns exemplos
iurii

Respostas:

69

Editar:

Olhe para a resposta de Vennesa em vez disso, que é uma explicação melhor.

Original:

Em primeiro lugar, o React.cloneElementexemplo só funciona se seu filho for um único elemento React.

Pois quase tudo {this.props.children}é o que você deseja. A clonagem é útil em alguns cenários mais avançados, onde um pai envia um elemento e o componente filho precisa alterar alguns adereços nesse elemento ou adicionar coisas como ref para acessar o elemento DOM real.

No exemplo acima, o pai que fornece ao filho não sabe sobre o requisito de chave para o componente, portanto, ele cria uma cópia do elemento que é fornecido e adiciona uma chave com base em algum identificador único no objeto. Para obter mais informações sobre o que a chave faz: https://facebook.github.io/react/docs/multiple-components.html

Morten Olsen
fonte
183

props.childrennão são os filhos reais; É o descriptordos filhos. Então, você não tem realmente nada para mudar; você não pode alterar nenhum adereço ou editar qualquer funcionalidade; você só pode read from it. Se você precisar fazer qualquer modificação, deverá criar new elementsusando React.CloneElement.

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

Um exemplo:

função de renderização principal de um componente, como App.js:

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

agora, digamos que você precise adicionar um onClicka cada filho de Paragraph; então em seu Paragraph.jsvocê pode fazer:

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

então, simplesmente você pode fazer isso:

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

Nota: a React.Children.mapfunção verá apenas os top levelelementos, ela não verá nenhuma das coisas que esses elementos renderizam; o que significa que você está fornecendo os acessórios diretos para as crianças (aqui os <Sentence />elementos). Se você precisar que os adereços sejam passados ​​adiante, digamos que você tenha um <div></div>dentro de um dos <Sentence />elementos que deseja usar o onClickadereço, então, nesse caso, você pode usar o Context APIpara fazê-lo. Faça do Paragraphprovedor e dos Sentenceelementos como consumidor.

Vennesa
fonte
11
Esta é uma resposta clara com um exemplo do mundo real. Precisa de mais votos positivos.
SeaWarrior404 de
1
Concordo com @ SeaWarrior404 - Acho que o OP estava procurando iterar em uma coleção em vez de em um único filho, porque sua interface de usuário tem "Usuários" / plural como título. Usar React.Children.mapem conjunto com React.cloneElement@vennesa aponta, é muito poderoso
jakeforaker
23

Na verdade, React.cloneElementnão está estritamente associado this.props.children.

É útil sempre que você precisa clonar os elementos react ( PropTypes.element) para adicionar / sobrescrever props, sem querer que o pai tenha conhecimento sobre os componentes internos (por exemplo, anexar manipuladores de eventos ou atribuir key/ refatributos).

Além disso, os elementos reactivos são imutáveis .

React.cloneElement (element, [props], [... children]) é quase equivalente a: <element.type {...element.props} {...props}>{children}</element.type>


No entanto, o childrenprop no React é especialmente usado para contenção (também conhecido como composição ), emparelhamento com React.ChildrenAPI e React.cloneElement, componente que usa props.children pode lidar com mais lógica (por exemplo, transições de estado, eventos, medições de DOM etc) internamente enquanto produz a parte de renderização para onde quer que seja usado, React Router <switch/>ou componente composto <select/>são alguns ótimos exemplos.

Uma última coisa que vale a pena mencionar é que os elementos de reação não se restringem a props.children.

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

Eles podem ser o que adereços que faz sentido, a chave foi definir um contrato bom para o componente, de modo que os consumidores de que pode ser dissociado dos detalhes de implementação subjacentes, independentemente se ele está usando React.Children, React.cloneElementou até mesmo React.createContext.

Xlee
fonte