Hartaithan.
Hartaithan.

Reputation: 376

(ReactJS) Separate trigger for each Collapsible element (react-collapsible)

How can I make a separate trigger for each Collapsible element with a single useState (preferably)?

CodeSandBox - https://codesandbox.io/s/separate-triggers-question-forked-vgs5b

App.js

import React from "react";
import Collapsible from "react-collapsible";

function App() {
  const database = [
    { id: 1, name: "Name1", description: "Desc1" },
    { id: 2, name: "Name2", description: "Desc2" },
    { id: 3, name: "Name3", description: "Desc3" },
    { id: 4, name: "Name4", description: "Desc4" },
    { id: 5, name: "Name5", description: "Desc5" }
  ];

  const [items, setItems] = React.useState(database);
  const [open, setOpen] = React.useState(false);

  return (
    <div className="tracker_master">
      {items.map((item, index) => (
        <div onClick={() => setOpen(!open)} key={item.id}>
          {item.name.toUpperCase()}

          <Collapsible open={open}>
            <div>{item.description.toUpperCase()}</div>
          </Collapsible>
        </div>
      ))}
    </div>
  );
}

export default App;

Upvotes: 1

Views: 1483

Answers (2)

Pavan
Pavan

Reputation: 858

If you want to use single useState then your single state object should manage open flag of all the items as shown below (I have updated your code and tested its working fine).

openFlags is single state which maintains the open flag of each item by id and use it for triggering collabse and expand the items independently.

import React from "react";
import Collapsible from "react-collapsible";

function App() {
  const database = [
    { id: 1, name: "Name1", description: "Desc1" },
    { id: 2, name: "Name2", description: "Desc2" },
    { id: 3, name: "Name3", description: "Desc3" },
    { id: 4, name: "Name4", description: "Desc4" },
    { id: 5, name: "Name5", description: "Desc5" }
  ];

  const [items, setItems] = React.useState(database);
  let initialOpenFlags = {}
   items.forEach((i) => {
    initialOpenFlags = {
      ...initialOpenFlags,
      [i.id]: false
    };
  });
  const [openFlags, setOpenFlags] = React.useState(initialOpenFlags);

  return (
    <div className="tracker_master">
      {items.map((item, index) => (
        <div
          onClick={() =>
            setOpenFlags({ ...openFlags, [item.id]: !openFlags[item.id] })
          }
          key={item.id}
        >
          {item.name.toUpperCase()}

          <Collapsible open={openFlags[item.id]}>
            <div>{item.description.toUpperCase()}</div>
          </Collapsible>
        </div>
      ))}
    </div>
  );
}

export default App;

Upvotes: 2

bas
bas

Reputation: 15722

To have seperate triggers for each item I recommend to abstract each element into its own component that has its own open state.

So you could do something like this:

const Item = ({ item }) => {
  const [open, setOpen] = React.useState(false);
  return (
    <div onClick={() => setOpen(!open)} key={item.id}>
      {item.name.toUpperCase()}

      <Collapsible open={open}>
        <div>{item.description.toUpperCase()}</div>
      </Collapsible>
    </div>
  );
};

function App() {
  const database = [
    { id: 1, name: "Name1", description: "Desc1" },
    { id: 2, name: "Name2", description: "Desc2" },
    { id: 3, name: "Name3", description: "Desc3" },
    { id: 4, name: "Name4", description: "Desc4" },
    { id: 5, name: "Name5", description: "Desc5" }
  ];

  return (
    <div className="tracker_master">
      {database.map((item) => (
        <Item item={item} key={item.id} />
      ))}
    </div>
  );
}

sandbox example

Upvotes: 2

Related Questions