Reputation:
I am trying to implement the following side toggle component.
I am maintaining two components to handle the flow. One being ToggleItems and the other being ToggleItem.
The open-close icons are being added using classes left
, right
for the header. And for the children, it's up
, down
. There is a parent state maintained and that gets passed down to the children.
I see on a child's up arrow and down arrow click, all the children get toggled. Can someone help me rectify this issue?
Sandbox: https://codesandbox.io/s/react-hooks-usestate-forked-3enpm
Upvotes: 1
Views: 235
Reputation: 16576
Your parent component can be updated to maintain an array of boolean values: one for each toggle.
Next, you'll want to pass down an index-specific change handler to each toggle component to make sure it's capable of changing the state.
import React, { useState } from "react";
import ToggleItem from "./ToggleItem";
type ToggleState = Record<number, boolean>;
const ToggleItems: React.FC = () => {
const [containerExpand, setContainerExpand] = useState(false);
const [headerExpand, setHeaderExpand] = useState<ToggleState>({});
const createToggler = (index) => () => {
const newHeaderExpand = { ...headerExpand };
newHeaderExpand[index] = !newHeaderExpand[index];
setHeaderExpand(newHeaderExpand);
};
return (
<div
className={`holder root-${!containerExpand ? "right" : "left"}`}
onClick={() => setContainerExpand(!containerExpand)}
>
<div style={{display: containerExpand ? "block" : "none"}} className="body">
{[0, 1, 2, 3].map((i) => (
<ToggleItem
key={i}
headerExpand={headerExpand[i] || false}
toggle={createToggler(i)}
/>
))}
</div>
</div>
);
};
export default ToggleItems;
Finally, you can change the child component to remove the redundant state and be able to toggle the parent state:
import React, { useEffect, useState } from "react";
const ToggleItem: React.FC = ({ headerExpand, toggle }) => {
return (
<>
<div>
<table className="legend-group-header-table">
<tbody>
<tr>
<td className="group-icon-holder">Sample1</td>
<td className="group-count-holder">3</td>
<td className="group-toggle-icon-holder">
<span
className={`group-toggle-icon ${
headerExpand ? "up" : "down"
}`}
onClick={() => toggle()}
></span>
</td>
</tr>
</tbody>
</table>
</div>
{headerExpand && (
<div className="legend-group-body">
<div> Test1 2hrs ago</div>
<div> Test2 2hrs ago</div>
</div>
)}
</>
);
};
export default ToggleItem;
Upvotes: 1