¿Cuándo debería usar React.cloneElement frente a this.props.children?

118

Todavía soy un novato en React y en muchos ejemplos en Internet, veo esta variación en la representación de elementos secundarios que encuentro confuso. Normalmente veo esto:

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

Pero luego veo un ejemplo como este:

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

Ahora entiendo la API, pero los documentos no aclaran exactamente cuándo debería usarla.

Entonces, ¿qué hace uno que el otro no puede? ¿Alguien podría explicarme esto con mejores ejemplos?

Amit Erandole
fuente
3
youtube.com/watch?v=hEGg-3pIHlE este tipo está mostrando cómo usa cloneElement. Mira esto para ver algunos ejemplos
iurii

Respuestas:

69

Editar:

En cambio, mire la respuesta de Vennesa , que es una mejor explicación.

Original:

En primer lugar, el React.cloneElementejemplo solo funciona si su hijo es un solo elemento React.

Porque casi todo {this.props.children}es el que quieres. La clonación es útil en algunos escenarios más avanzados, donde un padre envía un elemento y el componente hijo necesita cambiar algunos accesorios en ese elemento o agregar cosas como ref para acceder al elemento DOM real.

En el ejemplo anterior, el padre que le da al hijo no conoce el requisito de clave para el componente, por lo tanto, crea una copia del elemento que se le da y agrega una clave basada en algún identificador único en el objeto. Para obtener más información sobre lo que hace la clave: https://facebook.github.io/react/docs/multiple-components.html

Morten Olsen
fuente
183

props.childrenno son los niños reales; Es el descriptorde los niños. Así que en realidad no tienes nada que cambiar; no se puede cambiar ningún accesorio ni editar ninguna funcionalidad; solo puedes read from it. Si necesita realizar alguna modificación, debe crear new elementsutilizando React.CloneElement.

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

Un ejemplo:

función principal de renderizado de un componente como App.js:

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

ahora digamos que necesita agregar un onClicka cada hijo de Paragraph; entonces en tu Paragraph.jspuedes hacer:

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

entonces simplemente puedes hacer esto:

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

Nota: la React.Children.mapfunción solo verá los top levelelementos, no ve ninguna de las cosas que esos elementos representan; lo que significa que está proporcionando los accesorios directos a los niños (aquí los <Sentence />elementos). Si necesita que los accesorios se transmitan aún más, digamos que tendrá <div></div>uno dentro de los <Sentence />elementos que quiere usar el onClickaccesorio, entonces, en ese caso, puede usar el Context APIpara hacerlo. Hacer que el Paragraphproveedor y los Sentenceelementos sean consumidores.

Vennesa
fuente
11
Esta es una respuesta clara con un ejemplo del mundo real. Necesita más votos a favor.
SeaWarrior404
1
De acuerdo con @ SeaWarrior404 - Supongo que el OP estaba buscando iterar sobre una colección en lugar de un solo niño porque su interfaz de usuario tiene "Usuarios" / plural como título. Usar React.Children.mapjunto con React.cloneElementcomo señala @vennesa, es muy poderoso
jakeforaker
23

De hecho, React.cloneElementno está estrictamente asociado con this.props.children.

Es útil siempre que necesite clonar elementos de reacción ( PropTypes.element) para agregar / anular accesorios, sin querer que el padre tenga conocimiento sobre esos componentes internos (por ejemplo, adjuntar controladores de eventos o asignar key/ refatributos).

También los elementos de reacción son inmutables .

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


Sin embargo, el childrenaccesorio en React se usa especialmente para contención (también conocido como composición ), emparejamiento con React.ChildrenAPI y React.cloneElementcomponente que usa accesorios. Los niños pueden manejar más lógica (por ejemplo, transiciones de estado, eventos, mediciones DOM, etc.) internamente mientras ceden la parte de renderizado donde sea que se use, React Router <switch/>o componente compuesto <select/>son algunos buenos ejemplos.

Una última cosa que vale la pena mencionar es que los elementos de reacción no se limitan 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 />
      } />
  );
}

Pueden ser lo que los apoyos que tenga sentido, la clave es definir un contrato bueno para el componente, de manera que los consumidores de que pueden ser desacoplados de los detalles de implementación subyacentes, independientemente de si se está usando React.Children, React.cloneElemento incluso React.createContext.

Xlee
fuente