Stephan Bakkelund Valois
Stephan Bakkelund Valois

Reputation: 1072

Select all checkboxes / rows - React hooks app

I'm struggling with checking off all checkboxes at once within a table. I can check of individual checkboxes, however I also want the option to check off one "master checkbox", which checks all the other ones off.

I've created a simplyfied version of my React component: https://codesandbox.io/s/lively-wildflower-fknn9

There are two things with this I'm struggling with.

  1. If we remove the checked attribute on each of the two checkboxes, we can manually check each one and get it printed out to console. If we override the checked parameter by setting it to checked={checkAll} we can click the All checkbox, and both checkbox 1 and two gets checked off. However, it does not invoke the onChange attribute of the checkbox, which means I'm not able to record the checks.

  2. If I put on the checked={checkAll} attribute, I'm basically overriding the checkbox, which means the checkbox icon itself won't change from checked to unchecked. The onChange attribute is however invoked, and I'm able to record the change to the console.

I would like to check both checkboxes individually, and also use the "master checkbox" to check off both checkboxes, and invoke their onChange at the same time.

How can I go about doing this? Any suggestions would be super helpful

Upvotes: 4

Views: 12535

Answers (1)

cbdeveloper
cbdeveloper

Reputation: 31485

This is a way of handling it:

CodeSandbox link

This is the final behavior:

enter image description here

import React, { useState, useEffect } from "react";
import "./styles.css";

export default function App() {
  const [checkedAll, setCheckedAll] = useState(false);
  const [checked, setChecked] = useState({
    nr1: false,
    nr2: false
  });

  /* ################################################ */
  /* #### TOGGLES checK STATE BASED ON inputName #### */
  /* ################################################ */

  const toggleCheck = (inputName) => {
    setChecked((prevState) => {
      const newState = { ...prevState };
      newState[inputName] = !prevState[inputName];
      return newState;
    });
  };

  /* ###################################################### */
  /* #### CHECKS OR UNCHECKS ALL FROM SELECT ALL CLICK #### */
  /* ###################################################### */

  const selectAll = (value) => {
    setCheckedAll(value);
    setChecked((prevState) => {
      const newState = { ...prevState };
      for (const inputName in newState) {
        newState[inputName] = value;
      }
      return newState;
    });
  };

  /* ############################################# */
  /* #### EFFECT TO CONTROL CHECKED_ALL STATE #### */
  /* ############################################# */

  // IF YOU CHECK BOTH INDIVIDUALLY. IT WILL ACTIVATE THE checkedAll STATE
  // IF YOU UNCHECK ANY INDIVIDUALLY. IT WILL DE-ACTIVATE THE checkAll STATE

  useEffect(() => {
    let allChecked = true;
    for (const inputName in checked) {
      if (checked[inputName] === false) {
        allChecked = false;
      }
    }
    if (allChecked) {
      setCheckedAll(true);
    } else {
      setCheckedAll(false);
    }
  }, [checked]);

  /* ########################## */
  /* #### RETURN STATEMENT #### */
  /* ########################## */

  return (
    <div className="App">
      <div>
        <label>All</label>
        <input
          type="checkbox"
          onChange={(event) => selectAll(event.target.checked)}
          checked={checkedAll}
        />
      </div>
      <div>
        <label>1</label>
        <input
          type="checkbox"
          name="nr1"
          onChange={() => toggleCheck("nr1")}
          checked={checked["nr1"]}
        />
      </div>
      <div>
        <label>2</label>
        <input
          type="checkbox"
          name="nr2"
          onChange={() => toggleCheck("nr2")}
          checked={checked["nr2"]}
        />
      </div>
    </div>
  );
}

Upvotes: 7

Related Questions