creativated
creativated

Reputation: 211

how to disable a button if more than once check box is checked in React JS

I have to create a button that activates when a check box is checked and disables when unchecked. I was able to achieve this by the following code.

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

export default function App() {

  const [change, setChange] = useState(true);
function buttonHandler(){
  setChange(!change)
}

  return (
    <div className="App">
      <button disabled={change}>Click Me</button>

      <input type="checkbox" onChange={buttonHandler}/>
      <input type="checkbox" onChange={buttonHandler}/>
      <input type="checkbox" onChange={buttonHandler}/>
      
    </div>
  );
}

Now I have another challenge where I have to keep it disabled if more than 1 check box is checked. I tried to use object and array manipulation but it does not work. Any advice on how this can be achieved.

Upvotes: 0

Views: 4085

Answers (2)

Andy
Andy

Reputation: 63550

Wrap your input elements in a parent element like form or div.

Maintain state; an array that contains each box status (either true or false). When a box is changed call the handleChange which will update the state.

The button disabled property should call a function called isDisabled which will check to see if there are zero boxes checked or more than one box checked, returning true if the condition is matched.

const { useEffect, useState } = React;

function Example() {

  const [boxes, setBoxes] = useState([]);

  // In the text I suggested wrapping the inputs in
  // a parent element. This was so we could use the following
  // code to find its index within the list of inputs
  // without adding any more code to the JSX
  // Cribbed from https://stackoverflow.com/a/39395069/1377002
  function handleChange(e) {

    // Destructure the children from the parent of
    // the element that was changed (ie all the input elements)
    const { parentNode: { children } } = e.target;

    // Find the index of the box that was changed
    const index = [...children].indexOf(e.target);

    // Copy the state
    const newState = [...boxes];

    // Toggle the boolean at the index of
    // the `newState` array
    newState[index] = !newState[index];

    // Set the state with the updated array
    setBoxes(newState);
  }

  // `filter` the boxes that return true.
  // Return true if the length is 0 or > 1.
  function isDisabled() {
    const len = boxes.filter(box => box).length;
    return len === 0 || len > 1;
  }

  return (
    <div className="App">
      <button disabled={isDisabled()}>Click Me</button>
      <form>
        <input type="checkbox" onChange={handleChange}/>
        <input type="checkbox" onChange={handleChange}/>
        <input type="checkbox" onChange={handleChange}/>
      </form>
    </div>
  );

}

ReactDOM.render(
  <Example />,
  document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Additional documentation

Upvotes: 0

illiteratewriter
illiteratewriter

Reputation: 4333

import React from "react";
import{useState} from 'react'

export default function App() {

  const [checkboxStatus, setCheckboxStatus] = useState(Array(3).fill(false));

  function buttonHandler(index){
    let status = [...checkboxStatus];
    status[index] = !status[index]
    setCheckboxStatus(status)
  }

  return (
    <div className="App">
      <button disabled={checkboxStatus.filter(status => status === true).length != 1}>Click Me</button>
      {Array(3).fill(0).map((_, index) => <input type="checkbox" checked={checkboxStatus[index]} onChange={() => buttonHandler(index)}/>)}
    </div>
  );
}

You can do that by keeping track of the status of the checkbox rather than tracking the status of the button. This is because if you know the status of all the checkboxes, you can easily calculate the status of the button.

I have also taken the liberty of converting the checkbox to map it since the content is the same. You can do the same by passing the index to each of them. Something like <input type="checkbox" onChange={() => buttonHandler(0}/> and so on for each of the inputs.

Upvotes: 2

Related Questions