Gabriel Jiménez | Hace alrededor de 2 meses
Antes de comenzar a implementar un carrusel, es importante saber qué es y cuando conviene utilizarlo. Partiendo de ahí, el siguiente paso es pensar en su diseño, ya que todo diseño debería partir de una buena planificación. Con el diseño en mano, pasaremos a la implementación, donde aprendemos no solo a construir el carrusel desde cero, sino también a aplicar buenas prácticas en React. Finalmente, revisaremos algunas alternativas en el mercado, por si prefieres ahorra tiempo y esfuerzo.
Un carrusel es un elemento de interfaz que permite mostrar contenido en forma de diapositiva. Es decir, el usuario ve un elemento a la vez y puede avanzar o retroceder para explorar el resto del contenido.
Un carrusel es útil cuando necesitamos listar contenido de forma dinámica y secuencial. Por ejemplo:
Con lo visto anteriormente, podemos identificar las características principales de un carrusel. Entre ellas destacan:
Diapositivas. Cada diapositiva permite mostrar un contenido específico, como una imagen, texto o tarjeta.
Controles. Elemento que permiten avanzar o retroceder entre las diapositivas. Generalmente, las vemos como botones o flechas.
Indicadores. Marcadores que ayudan a saber cuántas diapositivas existen y en cuál nos encontramos.
Titulo y Subtítulos. Texto opcional que acompaña a cada diapositiva para dar más contexto al contenido mostrado.
Ya sabemos qué, cuándo conviene utilizarlo y las características principales que lo componente. Ahora toca ponernos creativos y diseñar una solución que podamos implementar paso a paso en React.
El diseño es uno de los procesos más sobrevalorados en el desarrollo de software, ya sea backend o frontend. Sin embargo, es uno de los más confiables para crear soluciones escalables.
En resumen, cada componente tiene una responsabilidad específica.
Cada componente es encargado de realizar una tarea específica. De esta forma, mantenemos el componente bien estructurado.
Bla, bla, bla… Muéstrame código… Lo sé, el diseño puede ser un proceso un poco tardado. Sin embargo, prácticamente ya hemos desarrollado casi el 80 % del componente y el 20 % es solo tirar código. Vayamos allá y veamos cómo implementarlo.
Comencemos definiendo cada uno de los componentes:
src/components/Carrusel 1: import React from 'react' 2: 3: const Carrusel = () => { 4: return(<div className='carrusel'></div>) 5: } 6: 7: export const Slide = () => { 8: return(<div className='slide'></div>) 9: } 10: 11: export const Control = () => { 12: return(<div className='control'></div>) 13: } 14: 15: export const Indicators = () => { 16: return(<div className='indicators'></div>) 17: } 18: 19: export default Carrusel;
Es bastante sencillo: agregamos una clase CSS a cada componente para al final darle estilos.
Ahora vamos a atacar cada uno de los componentes de forma individual.
Componente Indicators
De forma muy general, el componente permite cambiar de diapositiva. Además, visualiza el total de diapositivas y destaca la activa.
src/components/Carrusel . . . 44: export const Indicators = ({ slides , current , onChange }) => { 45: return ( 46: <div> 47: {slides.map((_, i) => ( 48: <button 49: key={i} 50: onClick={() => onChange && onChange(i)} 51: > 52: {i === current ? "●" : "○"} 53: </button> 54: ))} 55: </div> 56: ) 57: } . . .
Analicemos
Línea 45-56
Renderizamos el total de diapositivas y asociamos un evento click.
Línea 50
Notifica a Carrusel sobre el cambio de diapositiva.
NOTA: Si quieres aprender más sobre props, te recomiendo mi artículo: React props: buenas prácticas para código más limpio y escalable.
Continuemos con el siguiente componente.
Componente Control
Su responsabilidades es bastante sencilla: permitir cambiar de diapositiva y bloquear en caso de que no existan más.
src/components/Carrusel . . . 17: export const Control = ({ slides, current, onChange }) => { 18: const totalSlides = slides.length 19: 20: function next() { 21: if (totalSlides === 0) return 22: const target = current + 1 23: if (target < totalSlides && onChange) onChange(target) 24: } 25: 26: function prev() { 27: if (totalSlides === 0) return 28: const target = current - 1 29: if (target >= 0 && onChange) onChange(target) 30: } 31: 32: return ( 33: <div className="control"> 34: <button onClick={prev} disabled={current <= 0}> 35: Anterior 36: </button> 37: <button onClick={next} disabled={current >= totalSlides - 1}> 38: Siguiente 39: </button> 40: </div> 41: ) 42: } . . .
Siguiente componente.
Componente Slide
Debe mostrar el contenido de la diapositiva activa. Además, opcionalmente puede mostrar un título y/o subtítulo.
src/components/Carrusel . . . 7: export const Slide = ({ content, title, subtitle }) => { 8: return ( 9: <div className="slide"> 10: {title && <h3>{title}</h3>} 11: {subtitle && <p>{subtitle}</p>} 12: {content} 13: </div> 14: ) 15: } . . .
Listo.
Ahora solo falta crear el Carrusel.
Componente Carrusel
Es el encargado de coordinar la comunicación entre cada uno de los componente. Además, mantiene un estado de interno para saber cuál es la diapositiva activa.
Entre otras de sus tareas importantes está:
Veamos la implementación:
src/components/Carrusel . . . 3: const Carrusel = ({ children }) => { 4: let control = null 5: let slides = [] 6: let indicators = null 7: 8: const items = Children.toArray(children) 9: 10: items.forEach((child) => { 11: if (!React.isValidElement(child)) { 12: throw new Error("Carrusel solo puede renderizar componentes válidos.") 13: } 14: 15: switch (child.type) { 16: case Control: 17: control = child 18: break 19: case Slide: 20: slides.push(child) 21: break 22: case Indicators: 23: indicators = child 24: break 25: default: 26: throw new Error( 27: "NavBar solo puede renderizar hijos de tipo <Control />, <Slide />, o <Indicators />." 28: ) 29: } 30: }) 31: 32: const [currentSlide, setCurrentSlide] = useState(0) 33: 34: function handleCurrentSlide(index) { 35: setCurrentSlide(index) 36: } 37: 38: return( 39: <div className='carrusel'> 40: {cloneElement(control, { 41: slides: slides, 42: current: currentSlide, 43: onChange: handleCurrentSlide 44: })} 45: {slides.map((slide, index) => index === currentSlide ? slide : null)} 46: { 47: cloneElement(indicators, { 48: slides: slides, 49: current: currentSlide, 50: onChange: handleCurrentSlide 51: }) 52: } 53: </div> 54: ) 55: } . . .
Analicemos
Línea 4-30
Filtramos solo componentes de Control, Indicators y Slide. Solo debe existir un componente Control y Indicators, mientras que el Slide permite un arreglo.
Línea 32
Estado interno para mantener la diapositiva activa.
Línea 38-54
Renderizamos los componentes en una posición en particular.
Línea 40-44
Clonamos Control y le pasamos los props necesarios para su funcionamiento: slides, currentSlide y un callback para saber si internamente se ha modificado.
Línea 47-52
De igual manera que Control, clonamos Indicators y le pasamos los mismos props.
La forma de utilizarlo sería la siguiente:
// Con Control, Slides y Indicators. <Carrusel> <Control/> <Slide content='Slide 1'/> <Slide content='Slide 2'/> <Slide content='Slide 3'/> <Indicators/> </Carrusel> // Sin Control <Carrusel> <Slide content='Slide 1'/> <Slide content='Slide 2'/> <Slide content='Slide 3'/> <Indicators/> </Carrusel> // Indicators. <Carrusel> <Control/> <Slide content='Slide 1'/> <Slide content='Slide 2'/> <Slide content='Slide 3'/> </Carrusel>
Como un plus podríamos agregar un componente Slides donde agrupe los Slide, de esta forma el componente se vería más limpio y estructurado. Gracias a las responsabilidades bien definidas de cada componente, hacer un cambio es relativamente sencillo.
Alternativas y librerías recomendadas
En el mercado ya existen muchas librerías que cumplen con este objetivo. Sin embargo, siempre es importante tener noción sobre como funcionan las cosas, tal vez no tan técnicamente pero si de forma general.
A continuación te comparto varias alternativas.
Joy UI
Joy UI es una librería basada en Material UI, pero con un estilo propio.
NOTA: Material UI ofrece un set de componentes React ya listos para usarse en cualquier proyecto.
La librería de cajón que todo desarrollador frontend ha escuchado. Compuesta de varios componentes en Javascript, flexibles, fáciles de configurar y escalables.
En este caso, tenemos el componente Carrusel — no como el caso anterior, donde es un ejemplo muy básico.
Al dividir el Carrusel en subcomponentes como Control, Slide e Indicators, tu código se vuelve más claro, escalable y fácil de mantener. Esto te da una base sólida para después agregar estilos, personalizar la experiencia en móviles y extender la funcionalidad sin problemas.
Además, construir un Carrusel en React desde cero no solo es un buen ejercicio para entender cómo funciona la composición de componentes, también te permite crear soluciones a medida de tu aplicación.