2

I have a table where the first column has a checkbox in each row. I want to disable a button if more than one checkbox is selected or no check box is checked. It should be active only if 1 checkbox is checked

import React, { useState } from "react";
import "./styles.css";
function Example() {
  const [boxes, setBoxes] = useState([]);
  function handleChange(e) {
    const {
      parentNode: { children }
    } = e.target;
    const index = [...children].indexOf(e.target);
    const newState = [...boxes];
    newState[index] = !newState[index];
    setBoxes(newState);
  }
  function isDisabled() {
    const len = boxes.filter((box) => box).length;
    return len === 0 || len > 1;
  }
  return (
    <div className="App">
      <button disabled={isDisabled()}>Click Me</button>
      <table>
        <thead>
          <th>One</th>
          <th>Two</th>
          <th>Three</th>
        </thead>
        <tbody>
          <tr>
            <td>
              <input type="checkbox" onChange={handleChange} />
            </td>
            <td> two data</td>
            <td> three data</td>
          </tr>
          <tr>
            <td>
              <input type="checkbox" onChange={handleChange} />
            </td>
            <td> two data</td>
            <td> three data</td>
          </tr>
          <tr>
            <td>
              <input type="checkbox" onChange={handleChange} />
            </td>
            <td> two data</td>
            <td> three data</td>
          </tr>
        </tbody>
      </table>
    </div>
  );
}

export default Example;

I was able to make this work if all the checkboxes are in the same parent node. But in the case of a table, each checkbox is in a separate row.

3 Answers 3

1

You can give them all the checkboxes one name and this solution will work just fine

    import React, { useState } from "react";
function Example() {
  const [btnStatus, setBtnStatus] = useState(true);
  function handleChange(e) {
    const elements = document.getElementsByName('checkbox');
    let checkedCount = 0;
    elements.forEach((element)=>{
      if(element.checked){
        checkedCount ++;
      }
    })
  if(checkedCount > 1 || checkedCount === 0){
    setBtnStatus(true)
  }else{
    setBtnStatus(false)
  }
  }

  return (
    <div className="App">
      <button disabled={btnStatus}>Click Me</button>
      <table>
        <thead>
          <th>One</th>
          <th>Two</th>
          <th>Three</th>
        </thead>
        <tbody>
          <tr>
            <td>
              <input name="checkbox" type="checkbox" onChange={handleChange} />
            </td>
            <td> two data</td>
            <td> three data</td>
          </tr>
          <tr>
            <td>
              <input name="checkbox" type="checkbox" onChange={handleChange} />
            </td>
            <td> two data</td>
            <td> three data</td>
          </tr>
          <tr>
            <td>
              <input name="checkbox" type="checkbox" onChange={handleChange} />
            </td>
            <td> two data</td>
            <td> three data</td>
          </tr>
        </tbody>
      </table>
    </div>
  );
}

export default Example;
Sign up to request clarification or add additional context in comments.

Comments

0

You can assign ids to the checkboxes and follow them like this.

import React, { useState } from "react";
function Example() {
  const [boxes, setBoxes] = useState({});
  function handleChange(e) {
    const {
      target: { id, checked }
    } = e;
    setBoxes({ ...boxes, [id]: checked });
  }
  function isDisabled() {
    const { length } = Object.values(boxes).filter(Boolean);
    return length !== 1;
  }
  return (
    <div className="App">
      <button disabled={isDisabled()}>Click Me</button>
      <table>
        <thead>
          <th>One</th>
          <th>Two</th>
          <th>Three</th>
        </thead>
        <tbody>
          <tr>
            <td>
              <input id="1" type="checkbox" onChange={handleChange} />
            </td>
            <td> two data</td>
            <td> three data</td>
          </tr>
          <tr>
            <td>
              <input id="2" type="checkbox" onChange={handleChange} />
            </td>
            <td> two data</td>
            <td> three data</td>
          </tr>
          <tr>
            <td>
              <input id="3" type="checkbox" onChange={handleChange} />
            </td>
            <td> two data</td>
            <td> three data</td>
          </tr>
        </tbody>
      </table>
    </div>
  );
}

export default Example;

Comments

0

Your initial test (with all checkboxes in the same parent node) was working because you are relying that they are all in the same parent node here:

const {
  parentNode: { children }
} = e.target;

where "children" are the actual checkboxes. In the table example, each checkbox's parent has only one child.

I would suggest a more flexible solution like below, sending the checkbox index as a parameter to the onChange handler:

import React, { useState } from "react";

// use props to determine the number of records
function Example(props) {
  // make sure initial state is known and false
  const [boxes, setBoxes] = useState(new Array(props.recordsNo).fill(false));

  // index - the index of the checkbox
  function handleChange(index) {
    const newState = [...boxes];
    newState[index] = !newState[index];
    setBoxes(newState);
  }
  
  function isDisabled() {
    const len = boxes.filter((box) => box).length;
    return len === 0 || len > 1;
  }

  // records will probably come through props => generate here an example
  const records = [...Array(props.recordsNo).keys()];
  
  // generate records display 
  const rows = records.map((value, i) => {
    return (
      <tr>
        <td>
          <input type="checkbox" onChange={() => handleChange(i)} />
        </td>
        <td> {i}</td>
        <td> two data</td>
        <td> three data</td>
      </tr>
    )
  })

  return (
    <div className="App">
      <button disabled={isDisabled()}>Click Me</button>
      <table>
        <thead>
          <th>One</th>
          <th>Two</th>
          <th>Three</th>
        </thead>
        <tbody>
          {rows}
        </tbody>
      </table>
    </div>
  );
}

export default Example;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.