cover

¿Qué es composición en React?

author photo

Héctorbliss

@hectorbliss


Mira el video si prefieres:

robot logo saying hello

No te quedes atrás: Actualízate

Suscríbete para recibir información sobre nuevos frameworks, updates, eventos, tips, hacks y más.

React es muy popular, lo curioso del asunto es que una gran cantidad de programadores front end, no utilizan una de las características más potentes de React: la composición. 🧩 😱

React es una herramienta que nos permite trabajar con composición mucho mejor de lo que lo hacen otras herramientas y frameworks.

Con React podemos combinar componentes de manera muy poderosa. Y en esta entrada vamos a aprender algunas de las técnicas más profesionales para usar React en tan solo cuatro ejemplos. Ven, acompáñame. 🤓

🧸 Primer ejemplo supersimple

<App> -> <Navbar> -> <LoginButton>

Si quisiéramos crear este pequeño grupo de componentes y hacerlos trabajar juntos, lo más lógico sería consumirlos uno dentro del otro. Algo así:

// app.jsx import Navbar from './Navbar.jsx' export default function App(){ return <NavBar />; } // Navbar.jsx import LoginButton from './LoginButton.jsx' export default function Navbar(){ return <nav><LoginButton /></nav>; } // LoginButton.jsx export default function LoginButton(){ return <button value="Login" />; }

Sin embargo, con esto tan simple, ya hemos introducido algunos problemas. 😱

  1. Con lo que tenemos ahora, no podríamos utilizar el componente <NavBar> en alguna versión sin <LoginButton>.
  2. ¿Y qué pasa si quisiéramos pasar el usuario desde <App> como prop a <LoginButton>? Tendríamos que hacer prop drilling a través de <Navbar>.
  3. ¿O si quisiéramos agregar un elemento a la <Navbar> solo si alguna condición de <App> se cumple?

👶🏻 Utilizando a los niños

React tiene un nombre muy particular para los componentes descendientes. Se trata del prop children. 👶🏻👦🏽

Con children vamos a poder hacer nuestra <Navbar> completamente libre de dependencias y prop drilling.

Vamos a convertirlo en “agnostic”, término que los gringos usan para expresar que no está atada al UI de sus descendientes (children). 🤓

import Navbar from './Navbar.jsx'; import LoginButton from './LoginButton.jsx'; export default function App() { return ( <Navbar> <LoginButton /> </Navbar> ); } // NavBar.jsx export default function Navbar({children}){ return <nav>{children}</nav>; }

Este código ahora es mucho más fácil de probar y no tienes que maldecir al testing, gracias a que ahora te puedes concentrar en una sola cosa a la vez. 👓 (focus 😉).

🧰 Existen otras formas de hacerlo

Existen otras formas de usar la misma estrategia: Composición. 🧩

Una de ellas es pasar un ReactNode como prop. Es decir, podemos pasar JSX como un prop, este será utilizado en el JSX de su padre.

import Navbar from './Navbar.jsx'; import LoginButton from './LoginButton.jsx'; export default function App() { const isLogged = cualquierFetcherDeTuPreferencia() return ( <Navbar loginElement={<LoginButton isLogged={isLogged} >} /> ); } // NavBar.jsx export default function Navbar({loginElement}){ return ( <nav> <div className="algun layout especial" > {loginElement} </div> </nav>); }

Esta es una de las maneras más populares para hacer composición, pues es semántica y fácil de leer y entender. ⚡️

👴🏼 No podemos irnos sin mencionar a los HOCs

Esto que estamos por analizar, tuvo su gran momento; los HOCs (Higher-order component) estaban en boca de todos, pues este pattern aprovecha las ventajas de otro conocido como decorator-design-pattern.

En palabras simples: un componente de orden superior es normalmente una función que acepta un componente y devuelve una versión ampliada de este componente, tal vez, con una configuración específica en sus props. 👩🏻‍🍼

Veamos cómo se vería el grupo de componentes con el que hemos estado trabajando con este pattern:

// App.jsx import Navbar from "./Navbar.jsx"; import useCualquierCustomHook from "./useCualquierCustomHook"; import AlgunProvider from "./AlgunProvider"; // HOC. Esto puede vivir en un util/service const enhanceNavbar = (Component) => { const isLogged = useCualquierCustomHook(); return ( <AlgunProvider> <Component isLogged={isLogged} />; </AlgunProvider> ); }; export default function App() { return enhanceNavbar(Navbar); }

Es mucho más común utilizar este pattern con componentes de tipo class para administrar los props con mayor facilidad. Sin embargo, este pattern está en des-uso y prefiero solo mostrarte el concepto, antes que llenarte de detalles confusos para ti. 👩🏻‍🎨

👀 El profeta Dan Abramov habló sobre esto y definió cierto uso de los HOCs como mala práctica.

🍼 Por ultimo, funciones como children

Bueno, si el equipo de React se ha alejado de ciertos patterns que consideran perjudiciales. ¿Cuál sería entonces el pattern recomendado para hacer composición?

¡Excelente pregunta! Misma que respondería con: Las funciones como children. 🍕

Este pattern que vamos a ver, está presente en muchas de las bibliotecas React más populares, como: Formik o ReactQuery.

Se trata de proveerle al prop children, que ya conocemos, más que solo JSX. Podemos pasarle un objeto por ejemplo:

// App.jsx import Navbar from './Navbar.jsx'; import LoginButton from './LoginButton.jsx' export default function App() { const user = useCualquierFetcher() return <Navbar> <LoginButton >{user}</LoginButton> </Navbar> } // LoginButton.jsx export default function LoginButton({children}){ return ( <nav> <div className={children.layoutClass} > <LoginButton isLogged={children.isLogged} displayName={children.displayName} /> </div> </nav>); }

Claro, el uso de children como objeto, podría ser confuso, difícil de leer y perder sentido. Pero eso no le resta mérito a lo poderoso que es este método. 🤯

Aunque no usemos children para pasar un objeto, ahora sabemos que children podría ser muchas cosas. Así que, también podría convertirse en una función. Veámoslo con un nuevo ejemplo: 🤩

// App.jsx import Navbar from './Navbar.jsx'; import LoginButton from './LoginButton.jsx' import ShowAd from './ShowAd.jsx' export default function App() { return <Navbar> {({displayName, isLogged, isPremium})=>{ <LoginButton isLogged={isLogged} /> {isLogged && <> <span> Hola {displayName} </span> {!isPremium && <ShowAd />} </>} }} </Navbar> } // Navbar.jsx import useCualquierFetcher from "./useCualquierFetcher"; export default function Navbar({ children }) { const user = useCualquierFetcher(); return ( <nav> <div className="layout pal user menu">{children(user)}</div> </nav> ); }

Supongamos que nuestro componente <LoginButton> consigue sus propios datos, pero tal vez los necesitamos en el nivel de <App>. Podríamos pasarlos hacia arriba, si children fuera una función.

Puede ser que este ejemplo no sea el mejor, tal vez si lo intentamos con un map, vamos a verlo:

// LatinAmericanGreatAuthorsList.jsx export default function LatinAmericanGreatAuthorsList({ authors, render }) { return ( <ul> {authors.map((author) => ( <li key={author.id}>{render(author)}</li> ))} </ul> ); } // App.jsx import HighlightedAuthor from './HighlightedAuthor'; import Author from './Author'; export default function App(){ const authors = usandoCualquierMalditoFetcherXD(); return ( <LatinAmericanGreatAuthorsList authors={authors} > { author => author.displayName === 'Julio Cortazar' ? <HighlightedAuthor {...author} /> : <Author autor={autor} /> } </LatinAmericanGreatAuthorsList> ); }

Pon especial atención a cómo con este pattern <App> no tiene que pasar la data al componente <LatinAmericanGreatAuthorsList>. Solo utilizarla en su propio JSX ocultando por completo el map. ¿Cool ah? 😮

A este pattern se le conoce como “render prop pattern”. ✅

Y actualmente nos encontraremos con que se prefiere utilizar el prop render en lugar de children.

¡Dejen a los niños en paz, no les pasen objetos! 🩴

<LatinAmericanGreatAuthorsList authors={authors} render={(author) => author.displayName === "Julio Cortazar" ? ( <HighlightedAuthor author={author} /> ) : ( <Author author={author} /> ) } />;

Tal vez no le encuentres un uso para los componentes en los que trabajas ahora mismo, pero créeme, saber que este pattern existe y entender cómo puede serte útil, no sobra entre tu abanico de habilidades, además si lo dominas podrías entender mejor las herramientas open source que han estado apareciendo recientemente explotando estos patterns de composición.

Así es, dominar la composición con React, es también entender mejor al HTML.

El HTML no ha dejado la mejor idea de la web, tanto que lo utilizamos aún hoy en forma de JSX. Entonces, podemos decir que React es muy poderoso para hacer composición gracias al JSX, que a su vez es en extremo flexible gracias a HTML.

Nunca desprecies los fundamentos. 😇

Voy a compartir en el Discord de Fixtergeek un libro que te ayudará con estos fundamentos, no dejes de unirte, te dejo el link.

Abrazo. Bliss. 🤓

Enlaces relacionados

Discord Link

Dan Abramov sobre HOCs

Decorator pattern

banner

¿Quieres mantenerte al día sobre los próximos cursos y eventos?

Suscríbete a nuestro newsletter

Jamás te enviaremos spam, nunca compartiremos tus datos y puedes cancelar tu suscripción en cualquier momento 😉

robot logo saying hello
facebook icontwitter iconlinkedin iconinstagram iconyoutube icon

© 2016 - 2023 Fixtergeek