Checkbox examples
Checkboxes and radio buttons use the checked property to determine their UI state (true or false), with their value being an arbitrary name for that selection. What can be confusing about this is that the checked HTML attribute here was intended to just set which checkbox/radio was set initially. The browser would keep track of when the user checked/unchecked inputs, not updating the checked attribute, and then when the user submitted a form, the browser would include the name/value props for the checked inputs automatically. We do things quite differently in React, where we are required to set the checked attribute, and watch for changes through onChange, and then keeping track of the state ourselves by examining e.target.checked.
Checkbox Example with Hooks
Above you can see an example which initializes a list of checkboxes from a static list of US states. We map the list in getDefaultCheckboxes to create our initial state object array, which gets initialized when our useCheckboxes hook gets run at the beginning of our CheckboxRadioExample render method. We then name the array properties [checkboxes, setCheckboxes] as our state variable and setter.
From here, we have the custom hook return the list of checkboxes and a new callback setCheckbox which we create within the scope of the hook. The callback creates a copy of the current checkboxes list and sets the checked property of the passed index on the array. Then it sets the new array with the setCheckboxes setter.
At this point we have fully scoped the state logic of our checkbox list in the useCheckboxes hook. We're now free to use it in our app!
We render a list of CheckboxLabel (a <label>
created by styled-components) and a <Checkbox type="checkbox">
(a styled.input). The type HTML attribute is what makes this a checkbox. We also add an onChange attribute here which we then access the e.target.checked (current checkbox state) from the event handler.
When someone interacts with a checkbox by mouse OR keyboard actions, (try tabbing and hitting spacebar on a checkbox), the onChange handler is fired, and we call our hook to update the list of checkboxes. The cycle is complete.
import {useState} from 'react'; import styled from 'styled-components'; const checkboxesList = [ 'New Jersey', 'Maryland', 'Connecticut', 'Florida', 'Massachussets', ]; const getDefaultCheckboxes = () => checkboxesList.map(checkbox => ({ name: checkbox, checked: false, })); export function useCheckboxes(defaultCheckboxes) { const [checkboxes, setCheckboxes] = useState( defaultCheckboxes || getDefaultCheckboxes(), ); function setCheckbox(index, checked) { const newCheckboxes = [...checkboxes]; newCheckboxes[index].checked = checked; setCheckboxes(newCheckboxes); } return { setCheckbox, checkboxes, }; } const Checkbox = styled.input` margin: 0px 10px 0px !important; cursor: pointer; `; const CheckboxLabel = styled.label` cursor: pointer; display: block; font-weight: normal; `; export function Checkboxes({ checkboxes, setCheckbox }) { return ( <> {checkboxes.map((checkbox, i) => ( <CheckboxLabel> <Checkbox type="checkbox" checked={checkbox.checked} onChange={e => { setCheckbox(i, e.target.checked); }} /> {checkbox.name} </CheckboxLabel> ))} </> ); } export default function CheckboxRadioExample() { const checkboxes = useCheckboxes(); return ( <div> <Checkboxes {...checkboxes} /> <span> Value: {checkboxes.checkboxes .filter(t => t.checked) .map(checkbox => checkbox.name) .join(', ')} </span> </div> ); }