Componentes con Hijos Personalizables en React

En el artículo anterior creamos Header e hicimos que renderice Hello internamente. Hoy vamos a hacer que el contenido de Header se pueda personalizar como un prop de forma que podamos decidir si usar Hello o no.

Esto además nos va a evitar el uso de prop drilling, pero ¿Por qué querríamos evitar esto? En nuestro caso no hay ningún problema con usar prop drilling, pero a medida que agregemos más y más componentes que reciban y pasen props a sus hijos esto se puede llegar a volver un problema debido a que:

  • Complicaría refactorizar nuestros datos (props).
  • Terminaríamos pasando más props de los necesarios para evitar problemas de olvidarnos un prop.
  • No pasaríamos props que si son necesarios debido al abuso de defaultProps.
  • Dificultaría seguir el flujo de props en nuestra aplicación si terminamos renombrando props a mitad de camino.

Todo esto se puede evitar haciendo lo que vamos a hacer eso.

function Header(props) {
  return React.createElement(
    "header",
    null,
    React.createElement(Hello, { name: props.name })
  );
}

Header.propTypes = {
  name: PropTypes.string
};

Header.defaultProps = {
  name: "Mundo"
};

Ese es nuestro Header, vamos a hacer que en vez de recibir el prop name reciba un prop que vamos a llamar children y vamos a hacerlo obligatorio.

function Header(props) {
  return React.createElement("header", null, props.children);
}

Header.propTypes = {
  children: PropTypes.node.isRequired
};

Como vemos pasamos props.children como tercer elemento de React.createElement, esto es porque el contenido, como lo definimos en nuestros propTypes, es un nodo de React por lo que es cualquier cosa que sirva como hijo de un elemento.

Si actualizamos el ejemplo usando nuestro nuevo Header quedaría así:

function Hello(props) {
  return React.createElement("h1", null, `Hola ${props.name}!`);
}

Hello.propTypes = {
  name: PropTypes.string
};

Hello.defaultProps = {
  name: "Mundo"
};

function Header(props) {
  return React.createElement("header", null, props.children);
}

Header.propTypes = {
  children: PropTypes.node.isRequired
};

const element = React.createElement(Header, {
  children: React.createElement(Hello, { name: "Sergio" })
});

const node = document.getElementById("app");

ReactDOM.render(element, node);

Como vemos ahora Header no sabe que Hello recibe un prop name, el solo recibe el elemento creado a partir de Hello como su prop children y lo coloca dentro de <header>.

Con esto acabamos de mejorar como nuestro componente se combine de otros componentes lo que permitiría que Header reciba cualquier otro componente o elemento de React y lo envuelva en la etiqueta <header>.

En el siguiente artículo vamos a ver más sobre este prop children