La comunidad de React se está alejando de HOCs (componentes de orden superior) a favor de los render prop components (RPC). En su mayor parte, los componentes HOC y render prop resuelven el mismo problema. Sin embargo, los render prop components están ganando popularidad porque son más declarativos y flexibles que un HOC.
¿Qué es un HOC?
Caso de uso mas conocido de render prop components
Hay muchas cosas buenas acerca de los componentes de orden superior. Un HOC es una función que acepta un componente simple y devuelve un nuevo componente que se puede extender su funcionalidad. Los conceptos detrás de HOC son una combinación de la funcionalidad y la composición funcional de “mixins” (ahora en desuso de React). La idea es encapsular la funcionalidad común y hacerla reutilizable.
¿Mixins? Si no has oído hablar de los mixins es porque se supone que ya no debes usarlos. Hace mucho tiempo, el equipo de React decidió que los mixins se consideran obsoletos. En ese momento, le pidieron a la gente que considerara los componentes de orden superior como la solución.
Ahora la comunidad está impulsando los render prop components en su lugar. Antes de profundizar en los render prop components, comprendamos un poco cómo funciona HOC.
En general, el objetivo de un HOC es proporcionar props derivados a un componente secundario. Al igual que react-redux, esos props pueden ser valores o funciones simples.
El caso clásico: connect()
Quizás el HOC más familiar es la función connect()
de react-redux. Con este HOC, el store de Redux de su aplicación se puede “conectar” a un componente normal. La función connect()
proporciona una interfaz simple para derivar props del estado de Redux y pasar esos props al componente conectado.
Cada HOC proporciona al menos una de dos cosas al componente secundario:
- props derivados
- funciones tipo callback
En el contexto de connect()
, esto se relaciona con las funciones mapStateToProps
y mapDispatchToProps
.
Punto clave: un HOC realmente no “encapsula” su componente secundario, sino que extiende su componente. Es una diferencia sutil pero importante.
Una de las razones para preferir los componentes de render prop es que no subvierten el árbol normal de componentes de React.
El problema: abusar de HOC
A pesar de todos los beneficios de un HOC, existen algunos problemas con el patrón. Muchos de los problemas del patrón mixins
aún se aplican a HOC: puede hacer que el código sea más confuso. Un HOC bien diseñado como connect()
hace que el código sea mejor. Pero hay formas de abusar del patrón y hacer que el código sea más difícil de entender.
Los problemas surgen cuando los desarrolladores ven HOC como la solución a todos los problemas.
Por ejemplo, no es una buena idea usar un HOC para hacer algo que puede hacer un componente normal. En el ejemplo de connect()
, HOC es un ajuste natural. Pero, ¿y si añadimos un tipo diferente de funcionalidad?
A continuación, puede ver un abuso del patrón HOC. Estamos creando un nuevo MyThingRowComponent
usando un row
HOC. La clave a tener en cuenta es que row
en realidad no pasa ningún prop al componente envuelto.
El row
HOC se utiliza para envolver un MyThingComponent
para que siempre esté contenido en un <div />
con los nombres de clase correctos. Puede ver que lo aplicamos de manera muy similar a la forma en que usamos connect
. Pasamos alguna configuración y luego el componente que pretendemos extender.
¿Por qué no un row
HOC?
El problema se vuelve más claro cuando observa el código del row
HOC. Los componentes de orden superior rompen la forma normal en que JSX administra los componentes anidados; recuerde que un HOC crea un nuevo componente fusionado. Debido a la extraña naturaleza de los HOC, es importante usarlos solo cuando necesite props derivados.
En este caso, un row
HOC rompe las reglas básicas de lo que debe proporcionar un HOC.
Cosas que rompen las reglas de HOC:
- No proporcionar ningún prop derivado
- No proporcionar ningún callback props
Confuso y restrictivo:
- Crea un nuevo componente “encapsulado”, lo que complica el árbol de componentes
- Solo admite un solo hijo
- Requiere código de compatibilidad adicional para funcionar como un componente “normal”
Uso de un componente contenedor
¿Cómo aplicamos un componente contenedor en lugar de un HOC? Cuando nuestro “contenedor” es un componente regular, ¡tenemos múltiples opciones sobre cómo usarlo!
Un caso común sería utilizar manualmente <MyThing />
dentro del contenedor <Row />
. Aquí puede ver que estamos usando ambos componentes y al final creamos uno nuevo <Somewhere />
componente.
El beneficio aquí es que podemos seguir dando al implementador el control total.
La desventaja de este enfoque es que debe hacerlo en todos los lugares que utilice <MyThing />
.
Uso de render props
En el caso de nuestro componente Row
, podemos usar JSX declarativo en lugar de usar un HOC. Esto funciona porque no necesitamos que los hijos de Row
reciban props derivados. Las cosas se complican cuando necesitas que los hijos tengan que saber de los props derivados.
¿Cómo pasamos props derivados de un componente a sus hijos? La respuesta es utilizar render props.
En resumen, un componente render prop es una función que se utiliza para representar a sus elementos secundarios. En lugar de renderizar hijos normalmente, el componente render prop llama a la función render y usa el resultado como los hijos. Esto permite que el componente render prop proporcione props derivados a la función render.
¿Suena confuso? En la práctica, es muy similar a la forma en que funciona un HOC. Si alguna vez ha usado el
Idea clave: el uso de render props le permite a su contenedor pasar props derivados a sus hijos.
Resumen
En resumen, no deberías usar HOCs en tus aplicaciones React porque…
- Crea un nuevo componente “encapsulado”, lo que complica el árbol de componentes y la extensibilidad del mismo
- Solo admite un hijo por uso
- Requiere código de compatibilidad adicional para funcionar como un componente “normal”
- Dificultad para pasar props derivados
Lo que me encanta del patrón Render Prop es que puede encapsular la lógica de un componente sin sacrificar la personalización y la simplicidad en el JSX.
Recursos útiles para aprender más
Si te quedaron algunas dudas o lagunas, te recomiendo estos recursos: