Los menús acordeón tienen varios usos como, por ejemplo, mostrar una lista de preguntas frecuentes, o menús y sub-menús, o también las distintas sucursales de un empresa, etc.

En este artículo, veremos cómo hacer un menú acordeón en React, completamente desde cero, paso por paso y sin usar librerías externas.

Usaremos la sintaxis de React Hooks para hacer esta aplicación en React. Por lo que si no estás muy familiarizado con React Hooks, puedes dar un vistazo a mi artículo Introducción a React Hooks [en inglés] para aprender los fundamentos de Hooks.

Puedes ver la demo de la aplicación aquí.

Empecemos.

Configuración inicial del proyecto

Crea un nuevo proyecto create-react-app.

npx create-react-app react-accordion-demo

Una vez que el proyecto está creado, borra todos los archivos de la carpeta src y crea los archivos index.js, App.js y styles.css dentro de ella. También crea aquí una nueva carpeta llamada utils.

Abre el archivo styles.css y copia este código en ese archivo.

Cómo crear las primeras páginas

Abre el archivo src/App.js y añade el siguiente código dentro de él:

import React from 'react';

const App = () => {
  const accordionData = {
    title: 'Section 1',
    content: `Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quis sapiente
      laborum cupiditate possimus labore, hic temporibus velit dicta earum
      suscipit commodi eum enim atque at? Et perspiciatis dolore iure
      voluptatem.`
  };

  const { title, content } = accordionData;

  return (
    <React.Fragment>
      <h1>React Accordion Demo</h1>
      <div className="accordion">
        <div className="accordion-item">
          <div className="accordion-title">
            <div>{title}</div>
            <div>+</div>
          </div>
          <div className="accordion-content">{content}</div>
        </div>
      </div>
    </React.Fragment>
  );
};

export default App;

Aquí usamos las propiedades del objeto accordionData para mostrar el contenido del acordeón.

Para la propiedad content, usamos la sintaxis de la plantilla literal de ES6 (``) así podemos escribir en varias líneas. También usamos un texto lorem ipsum de ejemplo.

Luego, abre el archivo src/index.js y añade el siguiente contenido:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './styles.css';

ReactDOM.render(<App />, document.getElementById('root'));

Si ejecutas la aplicación usando el comando yarn start desde la terminal, verás la siguiente pantalla:

Initial Accordion

Cómo abrir y cerrar el menú acordeón

Como has podido ver arriba, pudimos mostrar un única sección como parte del menú acordeón. Por defecto el acordeón está abierta, pero no podemos cerrarlo. Así que vamos a agregar esa funcionalidad para abrirlo y cerrarlo.

Añade un nuevo estado dentro del componente como se muestra a continuación:

const [isActive, setIsActive] = useState(false);

Aquí hemos definido el estado isActive. Con esto, ocultaremos o mostraremos el contenido del acordeón.

También importa el hook useState al comienzo del archivo:

import React, { useState } from 'react';

Luego, para el div con la clase accordion-title, añade el handler onClick de la siguiente manera:

<div className="accordion">
  <div className="accordion-item">
    <div
      className="accordion-title"
      onClick={() => setIsActive(!isActive)}
    >
      <div>{title}</div>
      <div>{isActive ? '-' : '+'}</div>
    </div>
    {isActive && <div className="accordion-content">{content}</div>}
  </div>
</div>

Aquí estamos invirtiendo el valor del estado isActive cuando hacemos clic en el div accordion-title. Si el valor de isActive es false, lo ponemos en true y al revés.

Mostramos el signo + o - de acuerdo con el valor de isActive a través de un operador ternario.

Y si el valor del estado isActive es true entonces sólo vamos a mostrar el contenido del acordeón como en el ejemplo de abajo:

{isActive && <div className="accordion-content">{content}</div>}

Si actualizas la aplicación, verás la siguiente pantalla:

Open and close accordion

Al hacer clic en el título, el acordeón se abre y podemos volver a hacer clic sobre él para cerrarlo.

Cómo añadir varias secciones a un menú acordeón

Si tenemos varias secciones, copiar y pegar una y otra vez el mismo código JSX no funcionará tan bien como si tenemos un solo una sección.

Entonces, lo que vamos a hacer es crear un componente separado solamente para mostrar el acordeón. Luego, de acuerdo con la cantidad de secciones que tengamos, vamos a usar un bucle para recorrer el componente y así hacer que muestre varias secciones.

Crea un nuevo archivo llamado Accordion.js dentro de la carpeta src y luego añade el siguiente código dentro de este:

import React, { useState } from 'react';

const Accordion = ({ title, content }) => {
  const [isActive, setIsActive] = useState(false);

  return (
    <div className="accordion-item">
      <div className="accordion-title" onClick={() => setIsActive(!isActive)}>
        <div>{title}</div>
        <div>{isActive ? '-' : '+'}</div>
      </div>
      {isActive && <div className="accordion-content">{content}</div>}
    </div>
  );
};

export default Accordion;

Aquí, lo que hemos hecho es mover el estado y el div accordion-item del archivo App.js al archivo Accordion.js. Luego, pasamos las propiedades dinámicas title y content usando la sintaxis de desestructuración de ES6:

const Accordion = ({ title, content }) => {

Ahora, abrimos el archivo App.js y reemplazamos el código con este:

import React from 'react';
import Accordion from './Accordion';

const App = () => {
  const accordionData = [
    {
      title: 'Section 1',
      content: `Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quis sapiente
      laborum cupiditate possimus labore, hic temporibus velit dicta earum
      suscipit commodi eum enim atque at? Et perspiciatis dolore iure
      voluptatem.`
    },
    {
      title: 'Section 2',
      content: `Lorem ipsum, dolor sit amet consectetur adipisicing elit. Mollitia veniam
      reprehenderit nam assumenda voluptatem ut. Ipsum eius dicta, officiis
      quaerat iure quos dolorum accusantium ducimus in illum vero commodi
      pariatur? Impedit autem esse nostrum quasi, fugiat a aut error cumque
      quidem maiores doloremque est numquam praesentium eos voluptatem amet!
      Repudiandae, mollitia id reprehenderit a ab odit!`
    },
    {
      title: 'Section 3',
      content: `Sapiente expedita hic obcaecati, laboriosam similique omnis architecto ducimus magnam accusantium corrupti
      quam sint dolore pariatur perspiciatis, necessitatibus rem vel dignissimos
      dolor ut sequi minus iste? Quas?`
    }
  ];

  return (
    <div>
      <h1>React Accordion Demo</h1>
      <div className="accordion">
        {accordionData.map(({ title, content }) => (
          <Accordion title={title} content={content} />
        ))}
      </div>
    </div>
  );
};

export default App;

Lo que hicimos fue convertir el objeto accordionData en un arreglo de objetos. Luego, lo recorremos con un bucle usando el método map, además de pasarle las propiedades title y content al componente Accordion.

Si actualizas la aplicación, verás que se muestran las tres secciones y que podemos abrir y cerrar cada una de ellas:

final working accordion

Cómo refactorizar el código

Como has visto, con solo mover la sección del acordeón a un componente separado y pasar el contenido dinámico mediante props, pudimos crear un acordeón desde cero y que funciona.

Como es una buena práctica dejar la información estática en un archivo separado, vamos a mover el arreglo accordionData a un archivo distinto y luego importarlos desde App.js

Crea un nuevo archivo llamado content.js dentro de la carpeta utils y copia el siguiente código dentro de este:

export const accordionData = [
  {
    title: 'Section 1',
    content: `Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quis sapiente
    laborum cupiditate possimus labore, hic temporibus velit dicta earum
    suscipit commodi eum enim atque at? Et perspiciatis dolore iure
    voluptatem.`
  },
  {
    title: 'Section 2',
    content: `Lorem ipsum, dolor sit amet consectetur adipisicing elit. Mollitia veniam
    reprehenderit nam assumenda voluptatem ut. Ipsum eius dicta, officiis
    quaerat iure quos dolorum accusantium ducimus in illum vero commodi
    pariatur? Impedit autem esse nostrum quasi, fugiat a aut error cumque
    quidem maiores doloremque est numquam praesentium eos voluptatem amet!
    Repudiandae, mollitia id reprehenderit a ab odit!`
  },
  {
    title: 'Section 3',
    content: `Sapiente expedita hic obcaecati, laboriosam similique omnis architecto ducimus magnam accusantium corrupti
    quam sint dolore pariatur perspiciatis, necessitatibus rem vel dignissimos
    dolor ut sequi minus iste? Quas?`
  }
];

Ahora, abre App.js y reemplaza el código de ese archivo con este:

import React from 'react';
import Accordion from './Accordion';
import { accordionData } from './utils/content';

const App = () => {
  return (
    <div>
      <h1>React Accordion Demo</h1>
      <div className="accordion">
        {accordionData.map(({ title, content }) => (
          <Accordion title={title} content={content} />
        ))}
      </div>
    </div>
  );
};

export default App;

Con esto hemos importado la información estática desde un archivo externo y luego lo borramos del archivo App.js.

Entonces ahora el código se ve prolijo y fácil de leer, además de que continúa funcionando:

final working accordion

Cierre

Con esto, hemos terminado de hacer nuestra app.

Puedes encontrar el código completo de esta aplicación en este repositorio de GitHub.

¡Gracias por leer!

Si quieres aprender en detalle todo lo nuevo de ES6+, incluyendo let y const, promesas, distintos métodos de promesas, desestructuración de objetos y arreglos, funciones flecha, async/await, importar y exportar, y mucho más desde cero, dale un vistazo a mi libro Mastering Modern JavaScript. Este libro cubre todo lo necesario para aprender React y ayudarte a mejorar tanto en JavaScript como en React.

Mastering Modern JavaScript