Using the reactstrap documentation, I created a pseudo-accordion with collapsible cards. I want to be able to toggle the cards, so that when I click on the card header of list 2 once it will open, and it will close again if I click it consecutively. If I click list 1, list 2 should close and list 1 should open, as expected.
Accordion Component:
import React, { Fragment, useState } from 'react';
import { Collapse, CardBody, Card, CardHeader } from 'reactstrap';
const Lists = () => {
const [isOpen, setIsOpen] = useState('');
const [lists, setLists] = useState([
{
_id: 'id_abc',
item_names: ['item 1', 'item 2', 'item 3'],
list_name: 'List 2',
author_name: 'Samantha Samson',
},
{
_id: 'id_xyz',
item_names: ['item 1', 'item 1', 'item 2', 'item 16'],
list_name: 'List 1',
author_name: 'John Johnson',
},
]);
return (
<Fragment>
{lists.map((list) => (
<Card key={list._id}>
<CardHeader onClick={() => setIsOpen(list._id)}>
<h4>{list.list_name}</h4>
</CardHeader>
<Collapse isOpen={isOpen === list._id ? true : false}>
<CardBody>
<ul>
{list.item_names.map((item) => (
<li>{item}</li>
))}
</ul>
</CardBody>
</Collapse>
</Card>
))}
</Fragment>
);
I tried adding a function to the onClick that looked like this:
const handleToggle = (id) => {
if(isOpen === id) {
setIsOpen('');
} else {
setIsOpen(id);
}
}
and gave the header:
onClick={handleToggle(list._id)}
but that gave many a too many rerenders/infinite loop error.
I also tried slight variations of that function with the same result.
You have pretty much fixed it with this one:
const handleToggle = (id) => {
if (isOpen === id) {
setIsOpen("");
} else {
setIsOpen(id);
}
};
So great job! Where you made a mistake is calling it like so:
onClick={handleToggle(list._id)}
This will call the function on every render and since it updates a state variable, you will get infinite renders.
It should be:
onClick={() => handleToggle(list._id)}
Sandbox: https://codesandbox.io/s/hungry-jepsen-1ip0x?file=/src/App.js
Just FYI: You can simplify your ternary operator as:
isOpen={isOpen === list._id}