user16860065
user16860065

Reputation:

Toggling with respect to react hooks doesn't work

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

Answers (1)

Nick
Nick

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

Related Questions