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.
CodePudding user response:
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;
CodePudding user response:
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;
CodePudding user response:
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;