How to expand and collapse only the first item in accordion using react hooks?

Viewed 1306

Here's what I've tried doing

import React, { Fragment, useState } from "react";

const Accordion = ({ items }) => {
  const [activeIndex, setActiveIndex] = useState(null);
  const [display, setDisplay] = useState("");

  const handleChange = (index) => {
    setActiveIndex(index);
    setDisplay(display === "active" ? "" : "active");
  };

  const renderedItems = items.map((item, index) => {
    return (
      <Fragment key={item.title}>
        <div className={`${display} title`} onClick={() => handleChange(index)}>
          <i className="dropdown icon"></i>
          {item.title}
        </div>
        <div className={`${display} content`}>
          <p>{item.content}</p>
        </div>
      </Fragment>
    );
  });
  return (
    <div className="ui styled accordion">
      {renderedItems}
      {activeIndex}
    </div>
  );
};

export default Accordion;

But on clicking a single item, it expands or collapses all others. Also, I want only one expanded item at any given time, so if a user clicks another item the previous one should close automatically.

Here is my App.js

import React from "react";
import Accordion from "./components/Accordion";

const items = [
  {
    title: "What is React?",
    content: "React is a front end javascript library",
  },
  {
    title: "What is Angular?",
    content: "Angular is a front end javascript framework",
  },
  {
    title: "What is Vue?",
    content: "Vue is a front end javascript library",
  },
];

const App = () => {
  return (
    <div>
      <Accordion items={items} />
    </div>
  );
};

export default App;
1 Answers

For your issue :

// Means if display is active so nothing else it's active, so everything is active
setDisplay(display === "active" ? "" : "active");

The display variable is shared between each accordion bodies... Always the same

Do you really need an display var ?

import React, { Fragment, useState } from "react";

const Accordion = ({ items }) => {
  const [activeIndex, setActiveIndex] = useState(null);

  const handleChange = index => {
    setActiveIndex(activeIndex === index ? null : index);
  };

  const renderedItems = items.map((item, index) => {
    return (
      <Fragment key={item.title}>
        <div className={`${display} title`} onClick={() => handleChange(index)}>
          <i className="dropdown icon"></i>
          {item.title}
        </div>
        <div className={`${activeIndex === index ? 'active' : ''} content`}>
          <p>{item.content}</p>
        </div>
      </Fragment>
    );
  });
  return (
    <div className="ui styled accordion">
      {renderedItems}
      {activeIndex}
    </div>
  );
};

export default Accordion;
Related