P粉3223196012023-08-31 09:00:43
Here's how I did it: https://codesandbox.io/s/elastic-pateu-flwqvp?file=/components/Selectors.js
I abstracted the selection logic into a useSelection()
custom hook, which means the current selection can be found in store[key].selected
, where key
can be any key of selectors
.
items
, selected
, setSelected
and sectionLabel
are stored in each useSelection()
call In store[key]
, and passed to a <CustomCheckboxGroup />
component.
The relevant part is the handleCheck
function within this component, which sets the new selection based on the previously selected value: if the current item
is contained within the previously selected
value, remove it. Otherwise, add it.
More detailed explanation (why)
Looking closely at your code, it seems like you are confused about how the checkbox component works in React.
Thechecked attribute of
input
is controlled by a boolean
state. Generic example:
const Checkbox = ({ label }) => { const [checked, setChecked] = useState(false) return ( <label> <input type="checkbox" checked={checked} onChange={() => setChecked(!checked)} /> <span>{label}</span> </label> ) }
On each render, the checked
value of <input />
is set based on the current value of the checked
state. When the entered checked
changes (during user interaction), the status is not automatically updated. But the onChange
event is fired and we use it to update the state to the negative value of the state's previous value.
When dealing with the <CheckboxList />
component, we cannot use a single boolean value to control all checkboxes , we need to set a boolean value for each checkbox being rendered. So we create a selected array and set the
checked value of each
<input /> to
selected.includes(item) The value of (returns a
boolean).
selected array in every
onChange event. We check if
item is contained in the previous version of
selected. If present, filter it out. If it doesn't exist, add it:
const CheckboxList = ({ items }) => { const [selected, setSelected] = useState([]) const onChecked = (item) => setSelected((prev) => prev.includes(item) ? prev.filter((val) => val !== item) : [...prev, item] ) return items.map((item) => ( <label key={item}> <input type="checkbox" checked={selected.includes(item)} onChange={() => onChecked(item)} /> <span>{item}</span> </label> )) }Hope this solves some problems.
P粉0605283262023-08-31 00:36:19
You need to create specific handleOnChange function for each group. I have created a similar function for the Genre checkbox group and you can create it for other groups in the same way.
This is the processing function.
const handleOnChangeGenre = (e) => { let newValArr = []; if (e.target.checked) { newValArr = [...state.pillarGenre.split(","), e.target.value]; } else { newValArr = state.pillarGenre .split(",") .filter((theGenre) => theGenre.trim() !== e.target.value); } setState({ ...state, pillarGenre: newValArr.join(",") }); };
Pass this function to CustomCheckboxGroup as the handleOnChange attribute as shown below.
<CustomCheckboxGroup checkboxdata={genres} checkboxvalues={state.pillarGenre} value={state.pillarGenre} sectionlabel="Genre" onToggleChange={handleGenreSwitch} togglechecked={genreswitch} handleOnChange={handleOnChangeGenre} />
For testing, comment out your handleOnChange function.
See the complete working solution in the sandbox - Complete code