No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Aprende Ingl茅s, Programaci贸n, AI, Ciberseguridad y mucho m谩s.

Antes: $249

Currency
$209
Suscr铆bete

Termina en:

5 D铆as
8 Hrs
15 Min
44 Seg

React.Children y React.cloneElement

11/19
Recursos

Aportes 41

Preguntas 11

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

11.-React.Children y React.cloneElement


Para poder pasar propiedades especiales a los componentes hijos de nuestros componentes contenedores cuando hacemos composici贸n.


Cuando enviamos m谩s de un componente o elemento hijo al que use CloneElement, la app deja de funcionar y suelta un error. CloneElement necesita recibir un elemento de react, cuando children es m谩s de un componente entonces tenemos un array, para esto existe React.Children que nos ayuda a que CloneElement entienda sin importar cuantos elementos vienen en el props.children.

function TodoHeader({ children, loading }) {
  //No importa si viene un elemento, o dos o null siempre nos devuelve un array

  return (
    <header>
      {React.Children.toArray(children).map((child) =>
        React.cloneElement(child, { loading: loading })
      )}
    </header> //Por cada child vamos a llamar a clone element.
  ); //Crear elemento a partir de otro (elemento, objeto con las props que queramos que tenga)
}

No son las herramientas m谩s populares pero pueden ser muy 煤tiles cuando queremos compartir una o ciertas props a los componentes hijos de un componente contenedor.

Imagino un componente que se renderiza en diferentes partes pero en alguna de ellas toma algunas propiedades diferentes ya sea estilos o textos. se podr铆a en ciertas partes usar React.cloneElemet para no tener que modificar otros componentes padres del aplicativo.

馃梽锔 React.Children y React.cloneElement

Apuntes

React.cloneElement

  • Con esta caracter铆stica de React podemos crear elementos de Nodos React
  • Cabe aclarar que esta funciona con un unico nodo, en caso de aplicarla en un conjunto de los mismos podemos ayudarnos de React.Children

React.Children

  • Nos permite manipular la prop children entre uno de sus usos podemos volver un conjunto de nodos react a un array

Muy buena explicaci贸n! Solo un apunte, actualmente cloneElement est谩 desaconsejado por React y en su documentaci贸n te muestra diferentes alternativas: https://beta.reactjs.org/reference/react/cloneElement
Un saludo!

Me pareci贸 sencillo trabajar as铆 tambi茅n jeje, no se qu茅 opinen:

import React from "react";

function TodoHeader({ children, loading }) {
  return (
    <header>
      {
        [...children].map((child, index) => (
          React.cloneElement(child, { loading, key: index })
        ))
      }
    </header>
  );
}

export {
  TodoHeader
};

 {React.Children.toArray(children).map((child) => React.cloneElement(child, { loading }))}

P贸nganle un:

.TodoSearch__container {
   transition: 1s;
}

Y ver谩n que bonito

Recomiendo mucho el link que adjunto Juan de Medium! Buen铆simo, me lo dej贸 super claro

React.Children tiene varias utilidades para trabajar con props.children. Para profundizar en que hacen les dejo lo siguiente:
React.Children

React.Children.map( children, function() )

React.Children.forEach( children, function() )

React.Children.count( children )

React.Children.only( children )

React.Children.toArray( children )

Con respecto a React.cloneElement( children, { props } ) me sirve para cuando tengo varios hijos dentro de un componente y casualmente todos ellos usan las mismas props que el padre adem谩s de otras propias. Por tanto para evitar escribirle las mismas props a cada hijo, las clonamos del padre y con m茅todos de array las repartimos entre los hijos.
React.cloneElement

馃く despacio despacio cerebrito.
Wow, esto s铆 que me vol贸 la cabeza.

Los 鈥淩eact Children鈥 se usan principalmente para dar estructura y organizar el contenido dentro de un componente React. Al utilizar componentes en React, se puede dividir el contenido en diferentes partes y organizarlo de manera m谩s clara y sencilla. Adem谩s, esto permite reutilizar c贸digo de manera m谩s eficiente, ya que puedes crear componentes gen茅ricos que pueden ser utilizados en diferentes partes de tu aplicaci贸n.

React.cloneElement es una funci贸n de React que se utiliza para clonar un elemento y pasarle nuevas propiedades. Esto puede ser 煤til en casos donde quieras crear una copia de un componente existente y modificar algunas de sus propiedades, pero sin tener que crear un nuevo componente desde cero. Por ejemplo, si tienes un componente que muestra un bot贸n y quieres cambiar el color del bot贸n en una parte de tu aplicaci贸n, puedes utilizar React.cloneElement para clonar el componente del bot贸n y pasarle una nueva propiedad de color.

no se ustedes, pero me parece innecesario complicarme la vida xd

podria ser para evitar el DRY, pero no lo se

Est谩 bueno conocer este tipo de herramientas que no son tan utilizadas, pero existen y uno puede toparse con ellas en un proyecto laboral.

Para los que est茅n usando Typescript y sufran un poco con los tipos les comparto lo que hice.

  • Children en el componente de Header lo difin铆 como un array de ReactElement (Es mejor usar ReactElement ya que es m谩s espec铆fico que ReactNode).
  • A los componentes del searchbar y del t铆tulo les puse como nuleable la propiedad de 鈥渓oading鈥
interface TodoHeaderType {
    children : ReactElement[],
    loading : boolean,
}

function TodoHeader({children, loading} : TodoHeaderType) {
    return (
        <header>
            {
                React.Children
                    .map(children, child => React.cloneElement(child, {loading}))
            }
        </header>
    );
}

export default TodoHeader;

Para los que quieran una animaci贸n de carga que va acorde al proyecto a mi parecer les dejo un fade en css:

.TodoCounter--loading {
  animation: fade 1s infinite;
}

/* infinite fade animation */
@keyframes fade {
  0% {
    opacity: 0.5;
  }
  50% {
    opacity: 0.3;
  }
  100% {
    opacity: 0.5;
  }
}

React.Children y React.cloneElement , me parecieron bastante utiles para pasar props entre varios hijos y contenedores, con eso no tener que estar pasando todos los props a cada hijo directamente

Me funcion贸 sin el paso extra de React.Children.toArray(children)

TodoCounters desde el curso de introducci贸n ya habia puesto estos condicionales para evitar que saliera y el resto era css normal:

import React from 'react';
import './TodoCounter.scss';

function TodoCounter({loading, completedTodos, totalTodos}) {

  if (!loading) {
    if (totalTodos === 0) {
      return (
        <h2>
          Escribe tu primera tarea
        </h2>
      );
    } else if (totalTodos === completedTodos) {
      return (
        <h2 className='textCounter'>Felicidades, completaste todas las tareas!!!</h2>
      );
    } else {
      return (
        <h2>
          Has completado {completedTodos} de {totalTodos} tareas
        </h2>
      );
    }
  } else {
     return;
 }
}

  export { TodoCounter };

Me sirvi贸 la idea para el TodoSearch 鈥︷煈

Creo que puede ser util para manipular una gran cantidad de componentes children y es necesario agregarles una propiedad a todos o incluso definiendo a que componente darle la propiedad.

Esta herramienta acaba de reemplazar formalmente al hook useContext 馃攣

En mi app, le estaba enviando el estado 鈥渄arkMode鈥 a CADA componente para asignar estilos oscuros鈥 esta funcionalidad me arregla la vida!

Esto es lo que tenia tiempo queriendo saber como se hacia, pense no era posible algo asi, excelente clase.

Un uso interesante puede ser si se necesita que todos los componentes dentro de un container tengan cierto color o propiedad visual, y que si est谩n fuera de ese container no tengan ese color. O incluso poder definir ese color dependiendo del container. Se aplica el clone element a todos los hijos del container y se le a帽ade la propiedad deseada.

Me vol贸 la cabeza el uso de est谩s herramientas!

<h2 className="TodoCounter">{!loading && `Has completado ${completedTodos} de  ${totalTodos}`} TODOs</h2>

驴Qu茅 es lo que me encanta de estos cursos? que JuanDC nos ense帽a muchas formas de hacer lo mismo y eso es genial porque tienes varias opciones para solucionar un problema, gracias JuanDC!!!

BEM (Block Element Modifier) es un estandar para crear nombres de clases que sigue la estructura:

block__element--modifier

Por lo que la clase para el texto en TodoCounter podr铆a quedar algo as铆:

.TodoCounter__text--disabled {
  opacity: 25%;
}

siempre que intentaba poner children en un componente visual studio me importaba Children. Ahora por fin se para que es.

Yo lo hab铆a pensado algo as铆, para que lo mostrara seg煤n hab铆a Todos o no

<TodoHeader>
				<Title>Todo tasks</Title>
				{totalTodos > 0 && 
						<TodoSearch 
							search={searchValue} 
							setSearch={setSearch} />
				}
			</TodoHeader>

jejjejejej ese error del minuto 12:05 se solucionaba dandole exactamente el index del children


  { React.cloneElement(children[0], {loading}) }
  { React.cloneElement(children[1], {loading}) }

Inquietud

Buenas tardes a todos, tengo una inquietud porque en este curso no se esta dejando Archivos de la clase?, es muy ganador encontrar los archivos para un mejor entendimiento. O en su caso un repositorio de git de cada clase. Espero una repuesta agradezco si es del profesor @JUAN DAVID CASTRO

Una vez use React.Children y React.cloneElement para crear un componente de formulario. En el que pasandole ciertas propiedades terminada pasandole los valores a todos sus hijos (inputs)

Me parece que est谩 s煤per pr谩ctico para agregar soporte de idiomas! Dejo un ejemplo de c贸mo lo distribuyo en mi header, aunque no pude usar Children 馃槄 馃憞

Ejemplo: https://juliantoro91.github.io/TAPP-react-todo-app/

En App.js

    <TodoHeader
        loading={loading}
        languageSupport={languageSupport}
        languageShifter={
          <LanguageShifter
            saveLanguage={saveLanguage} />}
        todoCounter={
          <TodoCounter
            tasksState={tasksState}
          />}
      />

En el TodoCounter del header

<div className="header-todo-counter">{ react.cloneElement(todoCounter, { loading, languageSupport }) }</div>

Repositorio Github: https://github.com/juliantoro91/TAPP-react-todo-app

Me parece muy 煤til para hacer animaci贸n con clases de CSS, simplifica mucho la l贸gica que hay que implementar .-.

Esta brutal, para aquellos componentes cuyos hijos van a tener si o si X propiedad en mi caso聽 聽 聽 ```js <ItemsContainer nickName={nickName}> {isTodosExisting && <TodosRenderItems dataTodosFiltered={dataTodosFiltered} todos={todos} setTodos={setTodos} />} {isTodosFinished && <TodosRenderCongratulations nickName={nickName} />} </ItemsContainer> import React from "react"; const ItemsContainer = ({ children, nickName }) => { return (
{React.Children.map(children, child => child && React.cloneElement(child, { nickName }) )}
); }; export { ItemsContainer }; ```
Leyendo la documentaci贸n de React, veo que no recomiendan usar el cloneElement(). ![](https://static.platzi.com/media/user_upload/image-a826bbae-ebe4-43fe-b19d-e3bb8581af35.jpg) React recomienda pasar la data con Render prop, Context o crear un Custom Hook.

React.Children y React.cloneElement

.
Para poder pasar propiedades especiales a los componentes hijos de nuestros componentes contenedores cuando hacemos composici贸n, podemos utilizar React.Children y React.cloneElement.
.
Nos damos cuenta que al cargar los TODOs los componentes de TodoCounter y TodoSearch siguen un comportamiento incorrecto, puesto que el primero marca como completado 0 de 0 TODOs y el segundo nos permite buscar TODOs teniendo este que estar deshabilitado hasta que carguen los mismos.
.
Entonces un primer intento de solucionar este problema ser铆a agregar la propiedad Loading e internamente los componentes deber铆an manejar este estado para cambiar la opacidad de los mismos o deshabilitar el buscador mientras se est茅n cargando los TODOs.
.

<TodoHeader>
	<TodoCounter ... loading={loading} />
	<TodoSearch ... loading={loading} />
</TodoHeader>
function TodoCounter({ totalTodos, completedTodos, loading }) {
  return (
    <h2
      className={`TodoCounter ${!!loading && "TodoCounter--loading"}`}
    >
      Has completado {completedTodos} de {totalTodos} TODOs
    </h2>
  );
}
function TodoSearch({ searchValue, setSearchValue, loading }) {
  const onSearchValueChange = (event) => {
    console.log(event.target.value);
    setSearchValue(event.target.value);
  };

  return (
    <input
      className="TodoSearch"
      placeholder="Cebolla"
      value={searchValue}
      onChange={onSearchValueChange}
      disabled={loading}
    />
  );
}

.
Lo que se puede notar es que en TodoSearch seg煤n el estado de loading deshabilita el input por medio de la propiedad disabled y adem谩s se le agrega un peque帽o estilo de opacidad para distinguir cuando est谩n cargando los TODOs.
.
Hacemos lo mismo para el TodoCounter cuando est茅n cargando los TODOs por medio de la clase TodoCounter--loading.
.

.TodoSearch:disabled {
	opacity: 25%;
}
.TodoCounter--loading {
	opacity: 25%;
}

.
Si queremos manejar din谩micamente algunas clases de un elemento de un componente de React podemos hacerlo cambiando las comillas dobles por comillas invertidas dentro de unas llaves.
.
Entonces tendr铆amos la clase TodoCounter que siempre estar谩 presente por defecto, y adem谩s tendremos una peque帽a validaci贸n que verifica si la propiedad loading es verdadera, si ese fuera el caso se a帽ade por medio del operador l贸gico && (AND) a la clase TodoCounter--loading.
.
A esto se le llama agregar una clase condicionalmente.
.

<h2 className={`TodoCounter ${!!loading && "TodoCounter--loading"}`}>

.
Como podemos notar no es muy pr谩ctico estar pasando la propiedad loading a cada uno de los componentes hijos de TodoHeader e incluso en el futuro podr铆an ser m谩s.
.
Entonces es aqu铆 donde podemos hacer uso de React.Children y React.cloneElement para evitar ser redundantes a la hora de pasar propiedades comunes a los componentes hijos de un componente contenedor.
.
Primero a帽adimos la propiedad loading al componente contenedor TodoHeader y lo quitamos de las propiedades de los componentes hijos. Pero adem谩s necesitamos comentar por un momento el componente TodoSearch por fines educativos y m谩s adelante lo vamos a descomentar cuando implementemos la funcionalidad completa.
.

<TodoHeader loading={loading} >
	<TodoCounter
		totalTodos={totalTodos}
		completedTodos={completedTodos}
	/>
	{/* <TodoSearch
		searchValue={searchValue}
		setSearchValue={setSearchValue}
	/> */}
</TodoHeader>

.
Internamente en TodoHeader utilizamos React.cloneElement que lo que hace es clonar o crear un elemento a partir de otro, pas谩ndole como argumento a los hijos que tengamos del componente contenedor, en este caso ser铆a la propiedad children.
.
Adem谩s podemos enviar como segundo argumento un objeto con las propiedades que queramos que tenga nuestro clon, en este caso debemos enviar la propiedad loading.
.

function TodoHeader({ children, loading }) {
  return (
    <header>
      {
        React.cloneElement(children, { loading })
      }
    </header>
  );
}

.
Hecho esto la aplicaci贸n funcionar谩 correctamente pero solo si tenemos un componente hijo.
.

<TodoHeader loading={loading} >
	<TodoCounter
		totalTodos={totalTodos}
		completedTodos={completedTodos}
	/>
	<TodoSearch
		searchValue={searchValue}
		setSearchValue={setSearchValue}
	/>
</TodoHeader>

.
Si descomentamos el componente TodoSearch se rompe la aplicaci贸n, puesto que tendr铆amos m谩s de un componente hijo y por defecto React.cloneElement puede manejar solo un componente hijo dentro de children.
.
Para ello utilizaremos React.Children el cual nos ayudar谩 a utilizar React.cloneElement en el caso que el componente contenedor tenga a m谩s de un componente hijo.
.

function TodoHeader({ children, loading }) {
  return (
    <header>
      {
        React.Children
          .toArray(children)
          .map(child => React.cloneElement(child, { loading }))
      }
    </header>
  );
}

.
Entonces lo que se hizo fue utilizar React.Children que junto a la funci贸n toArray(children) lo que hace es tomar todos los componentes o elementos que est谩n en la propiedad children en un array que podemos manipular.
.
Esta implementaci贸n nos permite tener a TodoCounter y TodoSearch dentro de un array, e incluso no importa si el componente contenedor no tiene componentes hijos puesto que nos dar铆a un array vac铆o.
.
Finalmente, ya que tenemos el array podemos iterar el mismo por cada uno de sus elementos por medio de map y llamar a React.cloneElement y clonar cada uno de esos componentes hijos.
.
Gracias a esta implementaci贸n los componentes hijos recibir谩n autom谩ticamente la propiedad loading y la van a manejar internamente como se lo requiera.
.
Tambi茅n hay otras aplicaciones de React.Children como ser:
.

  • React.Children.map(): Similar al m茅todo map de arrays, permite mapear sobre los hijos y aplicar una funci贸n a cada uno.
React.Children.map(children, child => {
  // Operaciones con cada hijo
});
  • React.Children.forEach(): Similar a map, pero no devuelve un array.
React.Children.forEach(children, child => {
  // Operaciones con cada hijo
});
  • React.Children.count(): Devuelve el n煤mero total de hijos.
const numberOfChildren = React.Children.count(children);
  • React.Children.only(): Asegura que solo hay un hijo y devuelve ese hijo. Si hay m谩s de uno o ninguno, arrojar谩 un error.
const onlyChild = React.Children.only(children);

.
Adem谩s, un ejemplo com煤n de la aplicaci贸n de React.cloneElement podr铆a ser el siguiente.
.

function OriginalComponent(props) {
  return (
    <div style={{ color: props.color }}>
      {props.children}
    </div>
  );
}
function App() {
  const originalElement = <OriginalComponent color="blue">Hello, World!</OriginalComponent>;

  const clonedElement = React.cloneElement(originalElement, { color: 'red' });

  return (
    <div>
      {originalElement}
      {clonedElement}
    </div>
  );
}

.
En este ejemplo, OriginalComponent es un componente simple que toma un color y muestra ese color en su estilo. Luego, en el componente App, se crea un elemento original con el color azul y el texto 鈥淗ello, World!鈥. Luego, se clona ese elemento utilizando React.cloneElement y se cambia el color a rojo. Ambos elementos se renderizan en el componente App, y ver谩s dos bloques de texto con diferentes colores.

C贸digo de la App de la clase en TypeScript

import { FC } from 'react'

import { TodoButton } from "../TodoButton"
import { TodoCounter } from "../TodoCounter"
import { TodoItem } from "../TodoItem"
import { TodoList } from "../TodoList"
import { TodoSearch } from "../TodoSearch"
import { TodosError } from "../TodoError"
import { EmptyTodos } from "../EmptyTodo"
import { TodosLoading } from "../TodoLoading"
import { Modal } from "../Modal"
import { TodoForm } from "../TodoForm"
import { TodoHeader } from "../TodoHeader"
import { useTodos } from './useTodos'

const App: FC = () => {

  const {
    loading,
    error,
    searchedTodos,
    addTodo,
    completeTodo,
    deleteTodo,
    openModal,
    setOpenModal,
    completedTodos,
    totalTodos,
    searchValue,
    setSearchValue,
  } = useTodos();

  return (
    <>
      <TodoHeader
        loading={loading}
      >
        <TodoCounter
          completedTodos={completedTodos}
          totalTodos={totalTodos}
          // loading={loading}
        />
        <TodoSearch
          searchValue={searchValue}
          setSearchValue={setSearchValue}
          // loading={loading}
        />
      </TodoHeader>
      <TodoList
        loading={loading}
        error={error}
        onLoading={()=>(
          <>
            <TodosLoading />
            <TodosLoading />
            <TodosLoading />
          </>
        )}
        onError={()=>(
          <TodosError />
        )}
        onEmpty={()=><EmptyTodos />}
        searchedTodos={searchedTodos}
        searchText={searchValue}
        totalTodos={totalTodos}
        onEmptySearchResults={
          (searchText)=>(<p>No hay resultado para {searchText}</p>)
        }
        // render={(todo) => (
        //   <TodoItem
        //     key={todo.text}
        //     text={todo.text}
        //     completed={todo.completed}
        //     onComplete={() => { completeTodo(todo.text) }}
        //     onDelete={() => { deleteTodo(todo.text) }}
        //   />
        // )}
      >
        {
          (todo) => (
            <TodoItem
              key={todo.text}
              text={todo.text}
              completed={todo.completed}
              onComplete={() => { completeTodo(todo.text) }}
              onDelete={() => { deleteTodo(todo.text) }}
            />
          )
        }
      </TodoList>
      <TodoButton setOpenModal={setOpenModal} />

      {
        openModal && (
          <Modal>
            <TodoForm
              addTodo={addTodo}
              setOpenModal={setOpenModal}
            />
          </Modal>
        )
      }
    </>
  )
}



export default App

import { FC, ReactNode, Children, cloneElement, ReactElement } from "react";

interface Props {
    loading:boolean,
    children: ReactNode
}

const TodoHeader:FC<Props> = ({
    loading,
    children
}) => {

    return (
        <header>
            { Children.toArray(children).map((child)=>cloneElement(child as ReactElement, { loading } )) }
        </header>
    )
}

export { TodoHeader }
import { FC, Dispatch, SetStateAction } from "react";
import './TodoSearch.css';

interface Props {
    loading?:boolean,
    searchValue: string,
    setSearchValue: Dispatch<SetStateAction<string>>
}

const TodoSearch:FC<Props> = ({
    loading,
    searchValue,
    setSearchValue
}) => {

    return (
       <input 
        type="text" 
        className="TodoSearch" 
        placeholder="Ingresar nombre de tarea buscada" 
        value={searchValue}
        onChange={(event)=>{
            setSearchValue(event.target.value)
        }}
        disabled={loading}
    />
    )
}

export { TodoSearch }
.TodoSearch {
    background: #F9FBFC;
    border-radius: 2px;
    border: 2px solid #202329;
    margin: 0 24px;
    height: 64px;
    width: calc(100% - 62px);
    font-size: 24px;
    text-align: center;
    font-family: 'Montserrat';
    font-weight: 400;
    color: #1E1E1F;
    box-shadow: 0px 5px 50px rgba(32, 35, 41, 0.25);
}

.TodoSearch:disabled {
    opacity: 25%;
}

.TodoSearch::placeholder {
    color: #A5A5A5;
    font-family: 'Montserrat';
    font-weight: 400;
}

.TodoSearch:focus {
    outline-color: #61DAFA;
}

import { FC } from "react";
import './TodoCounter.css'

interface Props {
    loading?:boolean,
    completedTodos: number,
    totalTodos: number,
}

const TodoCounter:FC<Props> = ({
    loading,
    completedTodos,
    totalTodos
}) => {
 
    return (
        <h1 className={`TodoCounter ${loading && "TodoCounter--loading"}`}>Has completado <span>{completedTodos}</span>  de <span>{totalTodos}</span></h1>
    )
}

export { TodoCounter }
.TodoCounter {
    font-size: 24px;
    text-align: center;
    margin: 0;
    padding: 48px;
    font-weight: normal;
}

.TodoCounter--loading{
    opacity: 25%;
}

.TodoCounter span {
    font-weight: bold;
}

Resumen

  • React.cloneElement sirve para clonar un componente y agregarle propiedades.
  • React.Children.toArray al enviarle la prop children siempre nos devuelve un arreglo con nuestros elementos o componentes de children.

Pasar propiedades de un padre a todos sus hijos
.
La logica es que pasas la prop solo al padre y luego a todos los hijos, para eso usas React.cloneElement y React.Children
.

  • cloneElement recibe como primer parametro un elemento de react y lo clona, de forma que puedes a ese clon agregarle las propiedades
  • Children es una utilidad que nos permitir谩 trabajar con los hijos del componente. En este caso necesitamos retornar cada hijo como un elemento de react para que cloneElement le pueda agregar la prop que queremos
    .
{React.Children.map( children, child => React.cloneElement(child, {loading}))}

.
Esa linea de codigo鈥
.

  • Itera los hijos con el metodo map de React.Children
  • Funciona un poquito diferente, porque el primer argumento es el conjunto de hijos a iterar (podrias no hacerlo en todos los hijos, puedes antes de pasar por esta logica, filtrar los hijos a los que quieras aplicar esto)
  • El segundo argumento es una funcion que retorna cada hijo como un elemento react para que React.cloneElement reciba el hijo, lo clone, y le agregue la prop