skip to Main Content

I want to make table accordion .when I clicked on table row the next table data should display by default first one should be open and when I click another header there content should open without closing previous one.

I have created the state:

const [active, setActive] = useState(false);
  const handleClick = () => {
    setActive(!active);
  };
  return (
    <div>
      <div>
        <table className="table border">
          <tr>
            <td colSpan={3} className="header" onClick={handleClick}>
              Winfield
            </td>
          </tr>
          {active && (
            <tr>
              <td>USJN - Tifton </td>
              <td>12 </td>
              <td> 800</td>
            </tr>
          )}

          <tr>
            <td colSpan={3} className="header" onClick={handleClick}>
              Helena
            </td>
          </tr>
          {active && (
            <tr>
              <td>USJN - Tifton USJN - Tifton USJN - Tifton USJN - Tifton </td>
              <td>12 </td>
              <td> 800</td>
            </tr>
          )}
        </table>
      </div>
    </div>
  );

2

Answers


  1. There is plenty of ways doing this.

    Typically, you will want to iterate over and array with the table data instead of creating each row manually.

    Define an object with the data you need.

    const tableData = [
        {
            id: 1,
            name: "Winfield",
            value: "USJN - Tifton",
            numberA: 12,
            numberB: 800,
            active: true,
        },
        {
            id: 2,
            name: "Helena",
            value: "USJN - Tifton",
            numberA: 15,
            numberB: 600,
            active: false,
        },
    ]
    

    With this defined data, instead of creating each Table Row for yourself, iterate over this array creating them dinamically.

    Set the tableData as the state and notice the active value (true for the first value in the array, false for the others). This will make your first row open by default.

    function Accordion() {
    
        const [data, setData] = useState(tableData);
        const handleClick = (id) => {
            setData((data) => data.map(row => row.id === id ? {...row, active: !row.active} : row));
        };
    
        return (
            <div>
                <div>
                    <table className="table border">
                        {tableData.map(row => {
                            return (
                                <>
                                    <tr key={row.id}>
                                        <td colSpan={3} className="header" onClick={() => handleClick(row.id)}>
                                            {row.name}
                                        </td>
                                    </tr>
                                    {row.active &&
                                        <tr>
                                            <td>{row.value}</td>
                                            <td>{row.numberA}</td>
                                            <td>{row.numberB}</td>
                                        </tr>
                                    })
                                </>
                            )
                        }}
                    </table>
                </div>
            </div>
        )
    }
    

    Your onClick handler passes the id of the row that need to be changed the active state. we updated the state switching active for the id and let react take care of rerendering the component, since state has updated.

    If you only will have two rows, you can go with

    const [activeRow1, setActiveRow1] = useState(false);
    const [activeRow2, setActiveRow2] = useState(false);
    

    And have two states separate: one for each row. But it will be a nightmare if you need a more rows.

    Login or Signup to reply.
  2. One way would be to extract the accordion row into it’s own separate component. This will allow you to have separates states for each row.

    function CollapsibleRow({ data, defaultIsOpen, children }) {
      const [isOpen, setIsOpen] = React.useState(defaultIsOpen);
    
      return (
        <React.Fragment>
          <tr>
            <td colSpan={3} className="header" onClick={() => setIsOpen(!isOpen)}>
              {data}
            </td>
          </tr>
          {isOpen && children}
        </React.Fragment>
      );
    }
    
    function App() {
      return (
        <div>
          <div>
            <table>
              <tbody>
                <CollapsibleRow data="Winfield">
                  <tr>
                    <td>USJN - Tifton </td>
                    <td>12 </td>
                    <td> 800</td>
                  </tr>
                </CollapsibleRow>
                <CollapsibleRow data="Helena">
                  <tr>
                    <td>
                      USJN - Tifton USJN
                    </td>
                    <td>12 </td>
                    <td> 800</td>
                  </tr>
                </CollapsibleRow>
              </tbody>
            </table>
          </div>
        </div>
      );
    }
    
    ReactDOM.render(<App />, document.getElementById("root"));
    table {
      table-layout: fixed;
      width: 100%;
      border-collapse: collapse;
    }
    
    td {
      border: 1px solid #ccc;
      padding: 0.5rem;
    }
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <div id="root"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search