Artigo original: How to Build an Accordion Menu in React from Scratch – No External Libraries Required

Tradução em português europeu

Existem várias maneiras de se utilizar menus acordeão, tais como exibir uma lista de FAQs (Perguntas frequentes, em português), exibir vários menus e submenus, exibir as localizações de uma empresa em particular, e assim adiante.

Neste artigo, verás como criar um menu acordeão em React completamente do zero, passo a passo, sem utilizar qualquer biblioteca externa.

Vamos utilizar a sintaxe de "React Hooks" para criar esta aplicação em React. Então, se não tiveres experiência em React Hooks, dá uma vista de olhos ao meu artigo An introduction to React Hooks (texto em inglês) para aprender os conceitos básicos dos Hooks.

Podes ver uma demonstração da aplicação aqui.

Então, vamos começar.

Configuração inicial do projeto

Cria um projeto utilizando create-react-app.

npx create-react-app react-accordion-demo

Assim que o projeto for criado, remove todos os ficheiros da pasta src e cria os ficheiros index.js, App.js e styles.css dentro da pasta src. Além disso, cria uma pasta com o novo utils dentro da pasta src.

Abre o ficheiro styles.css e adiciona os conteúdos daqui dentro dele.

Como criar as páginas iniciais

Abre o ficheiro src/App.js e adiciona o seguinte conteúdo dentro dele:

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;

Aqui, utilizamos as propriedades do objeto accordionData para exibir o conteúdo do acordeão.

Para a propriedade content, utilizamos a sintaxe de template literals do ES6 (``) de maneira a podermos distribuir o conteúdo por várias linhas e utilizamos algum texto lorem ipsum para preencher.

Agora, abre o ficheiro src/index.js e adiciona o seguinte conteúdo dentro dele:

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

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

Agora, se executares a aplicação utilizando o comando yarn start a partir do terminal, verás o seguinte ecrã:

accordion_initial

Como abrir e fechar o menu acordeão

Tal como podes ver acima, exibimos uma única secção como uma parte do acordeão. Por defeito, contudo, o acordeão está expandido e não podemos fechá-lo. Então, vamos adicionar a funcionalidade para abrir e fechar o acordeão.

Adiciona um novo estado dentro do componente, tal como mostrado abaixo:

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

Aqui, definimos o estado isActive. Com base nisso, vamos ocultar ou exibir o conteúdo do acordeão.

Importa também o hook useState no início do ficheiro:

import React, { useState } from 'react';

Agora, para a div com a classe accordion-title, adiciona o manipulador onClick, desta forma:

<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>

Aqui, estamos a inverter o valor do estado isActive quando clicamos na div accordion-title. Se o valor de isActive for false, estamos a defini-lo para  true, e vice-versa.

Também estamos a exibir os sinais + ou - com base no valor de isActive, utilizando o operador ternário.

Se o valor de estado de isActive for true, vamos exibir apenas o conteúdo do acordeão, tal como mostrado abaixo:

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

Agora, se verificares a aplicação, verás o seguinte ecrã:

open_close

Como podes observar, inicialmente, o acordeão está fechado. Quando clicamos no título, o acordeão abre e podemos clicar nele novamente para fechá-lo.

Como adicionar várias secções em acordeão

Isto funciona bem para uma única secção em acordeão. No entanto, se tivermos várias secções, não será bom copiar e colar o mesmo código JSX vezes sem conta com conteúdo diferente.

Então, vamos criar um componente separado para exibir apenas o acordeão. Depois, com base no número de secções, vamos iterar sobre o componente para exibir várias secções.

Cria um ficheiro Accordion.js dentro da pasta src e adiciona-lhe os seguintes conteúdos:

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;

Aqui, movemos o estado e a div accordion-item do ficheiro App.js para o ficheiro Accordion.js. Estamos a passar os props dinâmicos title e content para o componente utilizando a sintaxe de desestruturação do ES6, como isto:

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

Agora, abre o ficheiro App.js e substitui-o pelo seguinte conteúdo:

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;

Aqui, convertemos accordionData de um objeto para um array de objetos. Estamos a iterar sobre ele utilizando o método de mapeamento de array e a passar os title e content correspondentes para o componente Accordion.

Agora, se verificares a aplicação, verás que as três secções são exibidas e que podemos abrir e fechar cada secção, tal como mostrado abaixo:

final_working

Como refatorar o código

Então, tal como podes ver, ao mover simplesmente a secção do acordeão para fora para uma componente separado e ao passar o conteúdo dinâmico como props, conseguimos criar com sucesso uma versão funcional de um acordeão a partir do zero.

É uma boa prática manter a informação estática num ficheiro à parte. Então, vamos mover o array accordionData para um ficheiro diferente e importá-lo para App.js.

Cria um ficheiro chamado content.js dentro da pasta utils e adiciona-lhe o seguinte conteúdo:

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?`
  }
];

Agora, abre App.js e substitui-o pelo seguinte conteúdo:

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;

Aqui, apenas importamos a informação estática do ficheiro externo e removêmo-la do ficheiro App.js.

Então, agora o código está mais limpo e fácil de compreender e a funcionalidade está a funcionar como antes.

final_working--1-

Pontos finais

Terminamos a construção da funcionalidade da nossa aplicação.

Podes encontrar o código fonte completo desta aplicação no GitHub neste repositório.

Obrigado pela leitura!

Desejas aprender todas as funcionalidades do ES6+ em detalhe, incluindo let e const, promises, vários métodos de promises, arrays e desestruturação de objetos, funções de seta, async/await, importação e exportação e muito mais a partir do zero?

Consulta o meu livro "Mastering Modern JavaScript" (em inglês). Ele aborda todos os pré-requisitos para aprender React e ajuda-te a ficar mais competente em JavaScript e React.

freecodecamp_image-1