Reputation: 376
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
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
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>
);
}
Upvotes: 2