I prefer to use one useState function for each checkbox.
In the 'old days' before hooks, each component had only one state - and the setState function would merge that state with the object passed to setState. Hooks & useState does not do it that way. You can have many useState hooks in one component, and the state does not merge.
Here is some code you can paste into App.js created using create-react-app to show what I mean:
App.js
import React, { useState } from 'react';
function App() {
var [ firstCheckbox, setFirstCheckbox ] = useState(true); // true - first check box defaults to checked.
var [ secondCheckbox, setSecondCheckbox ] = useState(false); // false = second check box defaults to un-checked.
return (
<div style={{display:'flex', flexDirection: 'column'}}>
<label for='first-check-box'><input type="checkbox" checked={firstCheckbox} onChange={() => setFirstCheckbox(!firstCheckbox)}></input>First Check Box</label>
<label for='second-check-box'><input type="checkbox" checked={secondCheckbox} onChange={() => setSecondCheckbox(!secondCheckbox)}></input>Second Check Box</label>
</div>
);
}
export default App;
Another hint for make the chrome developer tools more useful:
When you use many useState hooks as I suggest, it can be hard to see which state hook is which. Each of the useState hooks just shows as useState: true without any labels. The following code identifies the useState hook in the Components tab of the developer tools by making the actual state a property of an object, with the key being a label to show in the chrome developer tools.
App.js:
import React, { useState } from 'react';
function App() {
var [ firstCheckbox, setFirstCheckbox ] = useState({firstCheckBox: true});
var [ secondCheckbox, setSecondCheckbox ] = useState({secondCheckBox: false});
return (
<div style={{display:'flex', flexDirection: 'column'}}>
<label htmlFor='first-check-box'>
<input type="checkbox"
checked={firstCheckbox.firstCheckBox}
onChange={() => setFirstCheckbox(
{firstCheckBox: !firstCheckbox.firstCheckBox}
)}>
</input>
First Check Box
</label>
<label htmlFor='second-check-box'>
<input
type="checkbox"
checked={secondCheckbox.secondCheckBox}
onChange={() => setSecondCheckbox(
{secondCheckBox: !secondCheckbox.secondCheckBox}
)}>
</input>
Second Check Box
</label>
</div>
);
}
export default App;
Above, I used the input element's onChange() event for clarity. This requires the user to click on the actual check box. I prefer to allow them to click anywhere on the label OR the checkbox. To do this we can use the label element's onClick event:
To prevent react complaining in the console about the inputs not being controller, I mark them as readOnly also.
App.js:
import React, { useState } from 'react';
function App() {
var [ firstCheckbox, setFirstCheckbox ] = useState({firstCheckBox: true});
var [ secondCheckbox, setSecondCheckbox ] = useState({secondCheckBox: false});
return (
<div style={{display:'flex', flexDirection: 'column'}}>
<label
htmlFor='first-check-box'
onClick={() => setFirstCheckbox({firstCheckBox: !firstCheckbox.firstCheckBox})}>
<input type="checkbox" checked={firstCheckbox.firstCheckBox} readOnly/>
First Check Box
</label>
<label
htmlFor='second-check-box'
onClick={() => setSecondCheckbox({secondCheckBox: !secondCheckbox.secondCheckBox})}>
<input type="checkbox" checked={secondCheckbox.secondCheckBox} readOnly/>
Second Check Box
</label>
</div>
);
}
export default App;