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 its 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?
import React, { useState } from "react";
import ToggleItem from "./ToggleItem";
export default const ToggleItems: React.FC = () => {
const [headerExpand, setHeaderExpand] = useState<boolean>(false);
return (
<div
className={`holder root-${!headerExpand ? "right" : "left"}`}
onClick={() => setHeaderExpand(!headerExpand)}
>
<div className="body">
<ToggleItem {...{ headerExpand }} />
<ToggleItem {...{ headerExpand }} />
<ToggleItem {...{ headerExpand }} />
</div>
</div>
);
};
import React, { useEffect, useState } from "react";
export default const ToggleItem: React.FC = ({ headerExpand }) => {
const [itemClick, setItemClick] = useState<boolean>(headerExpand);
useEffect(() => {
setItemClick(headerExpand);
}, [headerExpand]);
return (
<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 ${itemClick ? "up" : "down"}`}
onClick={() => setItemClick(!itemClick)}
>
</span>
</td>
</tr>
</tbody>
</table>
</div>
{itemClick && (
<div className="legend-group-body">
<div> Test1 2hrs ago</div>
<div> Test2 2hrs ago</div>
</div>
)}
</div>
);
};
Also when parent is closed, and if I click on any of the down
arrows of children, the parent should be set to right
icon and corresponding child's data shoudld be visible with down
icon, like in the second picture
CodePudding user response:
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;