В этой статье мы сделаем простой аккордеон в реакции. Аккордеон полезен, когда нам нужно создать интерактивное приложение для пользователя. По сути, мы создадим кучу вопросов, и если мы нажмем на вопрос, то div развернется и покажет нам ответ.

Так что это будет очень забавный проект, и, как новичку, он научит нас большему, потому что мы будем использовать какой-то дополнительный пакет, такой как стилизованный компонент. Кроме того, мы сделаем этот аккордеон очень динамичным, например, мы можем добавить вопрос и ответ в определенный компонент, и все эффекты будут отражены в приложении. Итак, давайте сделаем это шаг за шагом.

Предпосылки для создания аккордеона в React

  • Базовые знания ReactJS.
  • Базовые знания хуков React.
  • Базовые знания React props.
  • Хорошее знание компонентов React.

Настройка проекта

Теперь, во-первых, нам нужно добавить два пакета: (1) styled-components (2) react-icons.

Затем нам понадобятся некоторые базовые компоненты, такие как Accordion.js, куда мы добавим основную логику приложения. И компонент Data.js, куда мы будем добавлять вопросы и ответы. Нам нужно изменить компонент приложения, здесь мы импортировали Accordion, и мы только что вызвали Accordion в операторе возврата.

import './App.css';
import Accordion from './Components/Accordion';
function App() {
  return <Accordion />;
}
export default App;

Добавление данных в компонент

Теперь, прежде чем добавлять основную логику, мы добавим наши данные, такие как вопрос и ответ, в компонент данных. Мы добавили только три данных, и можно добавить больше данных, которые будут отражаться в приложении.

export const Data = [
  {
    question: 'What do you call a dog magician?',
    answer: 'A labracadabrador.'
  },
  {
    question: 'What do you call a funny mountain?',
    answer: 'Hill-arious.'
  },
  {
    question: 'What did the astronaut say when he crashed into the moon?',
    answer: 'I Apollo-gize.'
  }
];

Импорт необходимых компонентов

Теперь давайте перейдем к нашему основному компоненту, Accordion.js. Здесь мы импортировали наш компонент данных, чтобы получить доступ к вопросам и ответам из него. Затем мы импортировали styled-components для создания стилей и базового CSS, при этом нам не нужно добавлять CSS в какой-либо другой файл. Затем мы импортировали значки реакции, чтобы получить доступ к значкам, таким как + и -, которые будут полезны в списке аккордеона.

import React, { useState } from 'react';
import { Data } from './Data';
import styled from 'styled-components';
import { IconContext } from 'react-icons';
import { FiPlus, FiMinus } from 'react-icons/fi';

Получение данных из Data.js

Теперь давайте перейдем к оператору return, здесь мы использовали этот метод Data.map() для получения всех данных. Затем мы добавили компонент ‹Wrap›, который на самом деле создан со стилизованным компонентом, в который мы добавим немного CSS. Здесь мы добавили тег заголовка, в котором мы получили вопросы по всем данным. А потом добавили ответы в абзац.

const AccordionSection = styled.div`
`;
const Container = styled.div`
  
`;
const Wrap = styled.div`
  
`;
return (
    <IconContext.Provider value={{ color: '#00FFB9', size: '25px' }}>
      <AccordionSection>
        <Container>
          {Data.map((item, index) => {
            return (
              <>
                <Wrap >
                  <h1>{item.question}</h1>
        
                </Wrap>
                <p>{item.answer}</p>
              </>
            );
          })}
        </Container>
      </AccordionSection>
    </IconContext.Provider>
  );
};

Стилизация компонента

Хорошо, мы успешно получили наши данные, теперь мы добавим немного CSS в стилизованные компоненты. Здесь мы, как всегда, добавили общий CSS, который вы можете просто изменить, чтобы получить свои любимые эффекты. Так что здесь мы не делали ничего особенного, как обычный CSS, поэтому перейдем к другой части логики.

const AccordionSection = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  height: 100vh;
  background: #fff;
`;
const Container = styled.div`
  position: absolute;
  top: 30%;
  box-shadow: 2px 10px 35px 1px rgba(153, 153, 153, 0.3);
`;
const Wrap = styled.div`
  background: #272727;
  color: #fff;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  text-align: center;
  cursor: pointer;
  h1 {
    padding: 2rem;
    font-size: 2rem;
  }
  span {
    margin-right: 1.5rem;
  }
`;
const Dropdown = styled.div`
  background: #1c1c1c;
  color: #00ffb9;
  width: 100%;
  height: 100px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border-bottom: 1px solid #00ffb9;
  border-top: 1px solid #00ffb9;
  p {
    font-size: 2rem;
  }
`;

Завершение приложения

Наш div вопроса выглядит немного хорошо, но теперь нам нужно скрыть ответ и показать его на основе условия. Для этого мы добавили состояние с именем clicked со значением false по умолчанию. Поскольку у нас есть вопрос в нашем компоненте Wrap, мы добавим событие щелчка, в котором мы будем вызывать функцию переключения.

В этой функции мы должны проверить, равен ли index значению clicked, что означает, что вопрос уже активен, тогда нам просто нужно закрыть его, обновив значение clicked на ноль. В противном случае мы обновим значение, по которому щелкнули, значением индекса.

Кроме того, мы добавили диапазон в компонент Wrap, где, если щелчок равен индексу, мы просто поместим значок минуса, иначе мы заменим значок значком плюса. Теперь для ответа мы поместим ответ в компонент раскрывающегося списка, который будет виден, если щелчок равен значению индекса. В противном случае мы не покажем ответ.

const [clicked, setClicked] = useState(false);
  const toggle = index => {
    if (clicked === index) {
      //if clicked question is already active, then close it
      return setClicked(null);
    }
    setClicked(index);
  };
  return (
    <IconContext.Provider value={{ color: '#00FFB9', size: '25px' }}>
      <AccordionSection>
        <Container>
          {Data.map((item, index) => {
            return (
              <>
                <Wrap onClick={() => toggle(index)} key={index}>
                  <h1>{item.question}</h1>
                  <span>{clicked === index ? <FiMinus /> : <FiPlus />}</span>
                </Wrap>
                {clicked === index ? (
                  <Dropdown>
                    <p>{item.answer}</p>
                  </Dropdown>
                ) : null}
              </>
            );
          })}
        </Container>
      </AccordionSection>
    </IconContext.Provider>
  );
};
export default Accordion;

Полный исходный код для создания аккордеона в React

App.js

import './App.css';
import Accordion from './Components/Accordion';
function App() {
  return <Accordion />;
}
export default App;

Accordion.js

import React, { useState } from 'react';
import { Data } from './Data';
import styled from 'styled-components';
import { IconContext } from 'react-icons';
import { FiPlus, FiMinus } from 'react-icons/fi';
const AccordionSection = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  height: 100vh;
  background: #fff;
`;
const Container = styled.div`
  position: absolute;
  top: 30%;
  box-shadow: 2px 10px 35px 1px rgba(153, 153, 153, 0.3);
`;
const Wrap = styled.div`
  background: #272727;
  color: #fff;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  text-align: center;
  cursor: pointer;
  h1 {
    padding: 2rem;
    font-size: 2rem;
  }
  span {
    margin-right: 1.5rem;
  }
`;
const Dropdown = styled.div`
  background: #1c1c1c;
  color: #00ffb9;
  width: 100%;
  height: 100px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border-bottom: 1px solid #00ffb9;
  border-top: 1px solid #00ffb9;
  p {
    font-size: 2rem;
  }
`;
const Accordion = () => {
  const [clicked, setClicked] = useState(false);
  const toggle = index => {
    if (clicked === index) {
      //if clicked question is already active, then close it
      return setClicked(null);
    }
    setClicked(index);
  };
  return (
    <IconContext.Provider value={{ color: '#00FFB9', size: '25px' }}>
      <AccordionSection>
        <Container>
          {Data.map((item, index) => {
            return (
              <>
                <Wrap onClick={() => toggle(index)} key={index}>
                  <h1>{item.question}</h1>
                  <span>{clicked === index ? <FiMinus /> : <FiPlus />}</span>
                </Wrap>
                {clicked === index ? (
                  <Dropdown>
                    <p>{item.answer}</p>
                  </Dropdown>
                ) : null}
              </>
            );
          })}
        </Container>
      </AccordionSection>
    </IconContext.Provider>
  );
};
export default Accordion;

Data.js

export const Data = [
  {
    question: 'What do you call a dog magician?',
    answer: 'A labracadabrador.'
  },
  {
    question: 'What do you call a funny mountain?',
    answer: 'Hill-arious.'
  },
  {
    question: 'What did the astronaut say when he crashed into the moon?',
    answer: 'I Apollo-gize.'
  }
];