Luigi
Luigi

Reputation: 404

ReactJS Function Component: accordion, allow for multiple selected open with useState

I've mocked a simple logic for an accordion collapsible panels in ReactJS. I'm trying to allow for multiple collapsible to be open but I'm not able to avoid all the collapsible to open and close at once no matter which collapsible has been clicked. This below is the logic for the accordion to allow only one collapsible at the time.

//Accordion.js
import React, { useState } from "react";
import styled, { css } from "styled-components";
import PropTypes from "prop-types";
import Collapse from "./Collapse";
import Header from "./Header";
const Accordion = ({ list, icon}) => {
  const [isActiveIndex, setActiveIndex] = useState(null);
  const toggleItem = index => {
    setActiveIndex(isActiveIndex === index ? null : index);
  };
  return (
    <Wrapper>
      {list.map((item, index) => {
        const checkOpen = isActiveIndex === index;

        return (
          <Container key={index}>
            <Header
              title={item.title}
              icon={icon}
              id={index}
              onClick={toggleItem}
            />
            <Body isOpen={checkOpen}>
              <Collapse isOpen={checkOpen}>{item.content}</Collapse>
            </Body>
          </Container>
        );
      })}
    </Wrapper>
  );
};

I've created the whole mock in CodeSandBox here: https://codesandbox.io/s/1r2mvk87q

For the initial accordion I'm using useState and checking for the active index - for the allow multiple I guess I should check the previous state of the clicked item but I'm not able to pass the clicked item as the only target for the state to be checked.

//AccordionMultiple.js
const AccordionM = ({ list, icon }) => {
  const [isOpen, setOpen] = useState(false);
  const toggleItemM = index => {
    setOpen(prevState => !prevState);
  };

  return (
    <Wrapper>
      {list.map((item, index) => {
        return (
          <Container key={index}>
            <Header
              title={item.title}
              icon={icon}
              id={index}
              onClick={toggleItemM}
            />
            <Body isOpen={isOpen}>
              <Collapse isOpen={isOpen}>{item.content}</Collapse>
            </Body>
          </Container>
        );
      })}
    </Wrapper>
  );
};

Upvotes: 1

Views: 4283

Answers (1)

Shubham Khatri
Shubham Khatri

Reputation: 282030

In order to allow for multiple collapsible column, you can make use of an object instead of a single index

const Accordion = ({ list, icon}) => {
  const [isActivePanel, setActivePanel] = useState({});
  const toggleItem = index => {
    setActivePanel(prevState => ({...prevState, [index]: !Boolean(prevState[index])}));
  };
  return (
    <Wrapper>
      {list.map((item, index) => {
        const checkOpen = isActivePanel[index];

        return (
          <Container key={index}>
            <Header
              title={item.title}
              icon={icon}
              id={index}
              onClick={toggleItem}
            />
            <Body isOpen={checkOpen}>
              <Collapse isOpen={checkOpen}>{item.content}</Collapse>
            </Body>
          </Container>
        );
      })}
    </Wrapper>
  );
};

Upvotes: 2

Related Questions