Héctor BlisS

@blissito

hace 2 años

5 formas de hacer composición con ReactJSEpisode 82·Season 4

Composición con React

1x

00:00

|

00:00

subscribe

share

more info

Composición en React para crear componentes realmente reusables

ships

Lo hemos hecho todo mal

Ya sé que exagero un poco cuando digo que todo lo hacemos mal con react pero lo digo pensando en todas las apps que he construido en estos casi 10 años con diferentes herramientas y todo el código que no puedo reusar y que en su momento parecía que sí.

Una vez que logras imaginar tus componentes con composición, un mundo de posibilidades se abre ante ti, posibilidades de reusabilidad muy potentes al grado de que puedes imaginar componentes que no sólo se rehúsan dentro de su código de origen, la composición consigue componentes tan bien estructurados y ordenados que se pueden escribir componentes que escapan del scope donde nacieron para convertirse en útiles para otros proyectos de cualquier tamaño, y esto es algo muy importante a tomar en cuenta. Por eso pienso que YO lo he hecho todo mal con React. Siempre se pensó para ser componentes reusables pero por alguna razón he estado construyendo cosas custom que no puedo compartir con React. Tal vez lo fácil que es usar ReactJS hace que nos pongamos creativos y hagamos piezas de arte que no se pueden compartir y esto no es malo, a menos que estés escribiendo código similar una y otra vez para diferentes proyectos como ha sido mi caso. ¿Tú qué piensas? ¿Crees que tus componentes son realmente reusables?

Directo al grano: #1 type

Hay mucho que decir si hablamos de patterns con React pero en esta entrada me quiero enfocar en la composición a nivel práctico y lo fácil que hace mi vida pensar en construir cosas así.

Quiero compartir 5 maneras de hacer composición que han estado con nosotros desde hace tiempo ya, pero voy a comenzar por la más moderna: el keyword TYPE.

Cuando tú generas un componente con React tanto de tipo función cómo de tipo clase (hace algunos ayeres) se genera un objeto con muchos atributos y algunos métodos, y uno de esos atributos es la llave type. Lo bello del atributo type de los componentes de React es que se pueden comparar directamente, es decir:

1 children[0].type === NavItem // true 2

Aquí NavItem funciona como nuestro componente imaginario de esta entrada. Esto es muy potente si nos detenemos a pensarlo principalmente porque podemos discriminar (filtrar) a cualquier componente que nos sea pasado en children que no coincida con el componente que buscamos.

1<NavContainer> 2 <p>lorem</p> 3 <NavItem>Texto</NavItem> 4 <p>lorem</p> 5</NavContainer> 6

Si tuviéramos una composición como la anterior y NavContainer no discrimina (filtra) esas <p> no podríamos tener control en el resultado final y tendríamos que confiar en que el desarrollador utilizará correctamente nuestro componente, o confiar en que le dará una leída a la documentación y saber que pasar como children y que no, (si existiese la documentación).

1const NavContainer = () => { 2 const items = children.filter(item=>item.type===NavItem) 3 const icons = children.filter(item=>item.type===Icon) 4 return ( 5 <section className="claseParaHacerloTodoBonito"> 6 <article className="algoDeFlexoGrid"> 7 {icons} 8 </article> 9 <article className="aquiSabemosQueVanSoloNavItems"> 10 {items} 11 </article> 12 </section> 13 ) 14} 15

La magia sucede en las líneas 2 y 3 donde se toma a los hijos que le son pasados al componente NavContainer y se filtran todos los que no coincidan con lo que se requiere además de lograr la separación de 2 tipos diferentes sin importar el orden en que fueron entregados. Esto es sin duda muy potente. Aquí te dejo un gran Tweet que lo simplifica, de nuestro profeta: Dan Abramov

#2 Props específicos

Esta forma de hacer composición es muy popular, porque te permite tener un poco más de control de una forma más tradicional, con props.

1const Drawer = ({left,right}) => { 2 return ( 3 <div className="estilosEspecificos"> 4 <div className="anidadoYDependiente"> 5 <Cards className="masAnidacion"> 6 {left} 7 </Cards> 8 </div> 9 <article>{right}</article> 10 </div> 11 ) 12} 13 14<Drawer 15 left={<Contacts />} 16 right={ 17 <div className="estilosInjectadosExtra"> 18 <Chat /> 19 </div> 20 } 21 /> 22 23

Lo que quiero mostrar con este pequeño ejemplo es que se puede lograr un mayor control pero de nuevo, es importante conocer la razón de ser de estos props y es necesario un conocimiento más profundo de estos componentes. A este tipo de componentes también se les llama especializados, ya que sus props soportan casos y usos específicos.

#3 Funciones como children

Esta forma de hacer composición con funciones es útil cuando necesitas entregar un grupo de elementos pero necesitas definir también cómo serán mostrados/tratados ese grupo de elementos, y es muy popular su uso en algunas librerías de React, la primera que se me viene a la mente es Formik a quien le entregas una función como children, y también pienso ahora mismo en ReactBeautifulDND.

1 const todos = [ 2 { label: 'Publicar en el blog', status: 'done' }, 3 { label: 'Enviar email', status: 'progress' }, 4 { label: 'Leer documentación', status: 'done' } 5 ]; 6 7const TodoList = ({children, todos}) => ( 8 <ul className='todo-list'> 9 { 10 todos.map((todo, i) => ( 11 <li key={ i }>{ children(todo) }</li> 12 )) 13 } 14 </ul> 15) 16 17 const App = () => { 18 <TodoList todos={ todos }> 19 { 20 todo => todo.status==='done' ? 21 <b>{ todo.label }</b> : 22 <span>todo.label</span> 23 } 24 </TodoList> 25 26 } 27

Nota la línea subrayada, children se usa como una función. Este método es muy potente pero también puede llegar a ser muy confuso y teniendo mejores formas de hacerlo si estás comenzando con composición puedes concentrarte en otros más sencillos, ya encontraras un caso de uso en el futuro, tal vez...

#4 Render prop

La propiedad render nos va ayudar a conseguir casi lo mismo que en el punto anterior pero un poco más obvio. Una biblioteca en la que puedo pensar que usa este método es la versión 6 de react-router. <Route path="invoices" element={<Invoices />} />

1const TodoList = ({render, todos}) => ( 2 <ul className='todo-list'> 3 { 4 todos.map((todo, i) => ( 5 <li key={ i }>{ render(todo) }</li> 6 )) 7 } 8 </ul> 9) 10 11const App = () => { 12<TodoList 13 todos={ todos } 14 render={ 15 todo => todo.status==='done' ? 16 <b>{ todo.label }</b> : <span>todo.label</span> 17 } /> 18} 19 20

#5 HOCs y HOFs

Una estrategia que se popularizó también fueron los HOCs o Higher Order Componentes y también las Higher Order Functions básicamente componentes y funciones que recibían un componente y devuelven uno nuevo (evitando la mutación) con algunos props extras inyectados. Recuerdas el HOF withRouter <= ¿Ya no te toco usarlo?

1// HOF 2const withClassName = (Component) => { 3 return ( 4 <Component className={'classNameEspecial'} /> 5 ) 6} 7//HOC 8function AuthWrapper(Component) { 9 return class extends React.Component { 10 render() { 11 if (this.props.isLoggedIn) { 12 return <WrappedComponent {...this.props} /> 13 } 14 return <p>You're not logged in ☹️</p> 15 } 16 } 17 } 18const ComponentWithClassName = withClassName(Component) 19const ComponentWithAuth = AuthWrapper(Component) 20

Esto está un poco en desuso pero no deja de ser una gran herramienta para no repetir código y agregarle capacidades a los componentes sin tener que modificarlos, lo cual ayuda a que los componentes se mantengan reusables, aunque también puede provocar tener que modificarlos para casos específicos.


Happy Coding! ❤ hectorbliss

Suscríbete a mi lista VIP

Y no te pierdas las actualizaciones

No te enviaré spam. Desuscríbete en cualquier momento.