In a form having multiple checkbox, I want to store the values of the check box in an useState array after clicking submit. Also the user may check/uncheck a check box multiple times before submitting the form. What can be the approach/code?
Every checkbox have a "checked" property, so in state you must have an array with all the checked checkboxes.
const [selectedCheckboxes, setSelectedCheckboxes] = useState([]);
For example you might store also the checkboxes on an array:
const checkboxes = [{name: 'cb1', label:'cb1'}, {name: 'cb2', label:'cb3'}, ...];
and all of them should have the same onChange method:
onCheckBoxChange = (event) => {
const selectedCheckboxes = [...selectedCheckboxes];
const isChecked = selectedCheckboxes.includes(event.target.name);
if (!isChecked) {
selectedCheckboxes.push(event.target.name);
} else {
selectedCheckboxes.splice(selectedCheckboxes.indexOf(event.target.name), 1);
}
setSelectedCheckboxes(selectedCheckboxes);
};
a common function that verify if a checkbox is checked:
isChecked = (cb) => selectedCheckboxes.includes(cb.name);
and every checkbox should look like:
<CheckBox
name={cb.name}
checked={isChecked(cb)}
onChange={onCheckBoxChange}
{...props}
/>
You can make it multiple ways. You can just make const checkboxOpenArray = [] where you will just store boolean values like [true, false, false, true] so later in the code where you probably .map through all checkboxes you just check if checkboxOpenArray[idx] where idx you get from .map. If it is true you just set checkbox to true. Same for onChange - you just find idx in the checkboxOpenArray and set it to false or true.
Related
I have a form that loads preset checkbox selections from the backend. I query the checkbox menu and iterate over it in JSX. Now I want to be able to select/deselect. To handle this, I start out by creating an array of objects matching the size of menu checkbox items like so:
useEffect(() => {
if (!profile) { // this checks if the reusable form is a create or update. If this is an update, I need to prefill an existing array of objects with added key/value pair of checked: true
setConcerns(Array(menuItems.length).fill({ checked: false }))
}
}, [menuItems]) // this happens first
// if it's an update form
useEffect(() => {
if (profile) {
const updatedConcerns = existingConcerns.map((c, i) => ({
...concerns,
c.id: c.id,
checked: true,
}))
}
}, [existingConcerns]) // this happens second
This sets up the concerns array for toggle.
In my JSX I have a Checkbox component:
{menuItems &&
menuItems.map((item: CheckboxProps, index: number) => (
<Checkbox
testID={item?.title}
label={item?.title}
checked={concerns && !!concerns[index]?.checked}
onValueChange={() => handleConcerns(index)}
/>
))}
And the handleConcerns method so far:
const handleConcerns = (index: number) => {
const concernsCopy = [...concerns]
concernsCopy[index].checked = !concernsCopy[index].checked
setConcerns(concernsCopy)
// the rest will determine how to add selected items to concerns
array for submit
}
This causes all of the checkboxes to toggle. This is just the start of pushing menu data into each index of concerns array but first need to get the checkboxes working and be able to have an array of chosen indices to submit to backend.
Resources for ProComponents that are being used in this demo.
ProForm same properties as ProModal
ProFormCheckBox.Group
ProFormSelect
I am trying to deselect all checkboxes every time I select a new item.
So for example if I select PCA from the dropdown and checked boxed 2.
Example 1
Then switched over to LSCA from the dropdown I want to deselect all checkboxes.
Example 2
Instead, what happens check box 2 is still selected.
Example 3
I have it set where each dropdown item has a different list of checkboxes set into it.
They are four different arrays. The more interesting parts are the three different useState. One controls the state of which dropdown item is selected. Another controls the state of which array of checkboxes should be displayed. The last controls the state of which checkboxes should be marked. Notes of interest in the code are
// Controls the state of the dropdown menu to be selected
const [selected, setSelected] = useState('');
// Controls the state of which array of checkboxes should be displayed
const [checkBoxes, setCheckBoxes] = useState([]);
// Controls the state of which checkboxes are checkmarked
const [markedCheckBoxes, setMarkedCheckBoxes] = useState([]);
Next code of interest is the function changeSelectOptionHandler which is run onChange of the ProFormSelect. This should run setMarkedCheckBoxes to set the state into an empty array so no boxes are selected.
/** Function that will set different values to state variable
* based on which dropdown is selected
*/
const changeSelectOptionHandler = (event) => {
// This should set the state of the setMarkedCheckBoxes to be empty
setMarkedCheckBoxes([]);
// Sets the state of which array of checkboxes should be displayed based on event
checkBoxOptions(event);
// Sets the state of which dropdown is selected based on the event
setSelected(event);
};
According to the docs I should set the value as what checkBoxes should be marked in ProFormCheckbox.Group
<ProFormCheckbox.Group
name="rows"
label="Select Rows"
options={checkBoxes}
onChange={(e) => {
console.log('state changes');
setMarkedCheckBoxes(e);
}}
// This is where I set which checkboxes should be marked with value
// initialValue={markedCheckBoxes}
value={markedCheckBoxes}
/>
I was able to use the React Dev Tools and confirm values are updated for markedCheckBoxes based on when a new dropdown item is selected which should be an empty array. I also tested that when I cancel or submit the modalForm that markedCheckBoxes is an empty array and is correctly displayed with setting the value on the ProFormCheckbox.Group. So I am stumped on how to correctly display what is on the value in ProFormCheckbox.Group after updating the select menu. Below is the full code snippet of said RowModal component.
import { PlusOutlined } from '#ant-design/icons';
import { Button, message } from 'antd';
import { useState } from 'react';
import ProForm, { ModalForm, ProFormSelect, ProFormCheckbox } from '#ant-design/pro-form';
import { updateRule } from '#/services/ant-design-pro/api';
const RowModal = ({ orderId, actionRef }) => {
/** Different arrays for different dropdowns */
const eca = ['1', '2', '3', '4', '5'];
const pca = ['1', '2'];
const lsca = ['1', '2', '3', '4', '5', '6'];
const mobility = ['1', '2', '3', '4'];
// Controls the state of the dropdown menu to be selected
const [selected, setSelected] = useState('');
// Controls the state of which array of checkboxes should be displayed
const [checkBoxes, setCheckBoxes] = useState([]);
// Controls the state of which checkboxes are checkmarked
const [markedCheckBoxes, setMarkedCheckBoxes] = useState([]);
/** Function that will set different values to state variable
* based on which dropdown is selected
*/
const changeSelectOptionHandler = (event) => {
// This should set the state of the setMarkedCheckBoxes to be empty
setMarkedCheckBoxes([]);
// Sets the state of which array of checkboxes should be displayed based on event
checkBoxOptions(event);
// Sets the state of which dropdown is selected based on the event
setSelected(event);
};
/** This will be used to create set of checkboxes that user will see based on what they select in dropdown*/
const checkBoxOptions = (event) => {
/** Setting Type variable according to dropdown */
if (event === 'ECA') setCheckBoxes(eca);
else if (event === 'PCA') setCheckBoxes(pca);
else if (event === 'LSCA') setCheckBoxes(lsca);
else if (event === 'Mobility') setCheckBoxes(mobility);
else setCheckBoxes([]);
};
return (
<ModalForm
title="Assign to Area and Row"
trigger={
<Button type="primary">
<PlusOutlined />
Assign
</Button>
}
autoFocusFirstInput
modalProps={{
destroyOnClose: true,
onCancel: () => {
setSelected('');
setCheckBoxes([]);
setMarkedCheckBoxes([]);
},
}}
onFinish={async (values) => {
const newValues = { ...values, order: orderId };
const req = await updateRule('http://127.0.0.1:3000/api/v1/floorPlans', {
data: newValues,
});
message.success('Success');
setSelected('');
setCheckBoxes([]);
setMarkedCheckBoxes([]);
actionRef.current?.reloadAndRest?.();
return true;
}}
// initialValues={{ rows: ['A'] }}
>
<ProForm.Group>
<ProFormSelect
request={async () => [
{
value: 'PCA',
label: 'PCA',
},
{
value: 'ECA',
label: 'ECA',
},
{
value: 'LSCA',
label: 'LSCA',
},
{
value: 'Mobility',
label: 'Mobility',
},
]}
// On change of dropdown, changeSelectOptionHandler will be called
onChange={changeSelectOptionHandler}
width="xs"
name="area"
label="Select Area"
value={selected}
/>
</ProForm.Group>
<ProFormCheckbox.Group
name="rows"
label="Select Rows"
options={checkBoxes}
onChange={(e) => {
console.log('state changes');
setMarkedCheckBoxes(e);
}}
// This is where I set which checkboxes should be marked with value
// initialValue={markedCheckBoxes}
value={markedCheckBoxes}
/>
</ModalForm>
);
};
export default RowModal;
Thanks in advance!
ProForm is a repackaging of antd Form
So you can use Form API for your purpose
import { useForm } from 'antd/lib/form/Form'
//...
const [form] = useForm();
// and then pass FormInstance in your component
<ModalForm
form={form}
//...
/>
// then in your handlers where you want to modify values use
form.setFieldsValue({
"fieldName": value
})
I'm working with ReactJS with Typescript and the goal is to have a checkbox component that adds and removes items to a string array according to whether the checkbox is selected or not. My current function only adds to the array, so when I select then unselect a checkbox the item is added twice. Thanks in advance.
Function:
const handleGroupChange = (groupOptions: any) => {
const existSelection = selectedGroups;
existSelection.push(groupOptions.target.value);
setSelectedGroups(existSelection);
}
};
Checkbox:
<FormControlLabel
control={
<Checkbox
color="primary"
onChange={e => handleGroupChange(e)}
value={"MATCHED_MENTORS"}
/>
}
check whether checkbox is checked or not on the onChange event.
const handleGroupChange = (groupOptions: any) => {
const existSelection = selectedGroups;
if (groupOptions.target.checked) {
existSelection.push(groupOptions.target.value);
} else {
var index = existSelection.indexOf(groupOptions.target.value);
if (index !== -1) {
existSelection.splice(index, 1);
}
}
setSelectedGroups(existSelection);
}
You need to check the checkbox boolean property whether it is checked or not. Please follow this example below and here is the Codesandbox
const [selectedGroups, setSelectedGroups] = useState([]);
const handleGroupChange = (groupOptions) => {
let existSelection = selectedGroups;
if (groupOptions.target.checked)
existSelection.push(groupOptions.target.value);
else
existSelection = existSelection.filter(item => item !== groupOptions.target.value)
console.log(selectedGroups);
setSelectedGroups(existSelection);
The thing here is that you are pushing a boolean as item therefore the existSelection array should look something like [true, false, true, false] more or less. That should be something to take a look at if it is the behavior you want.
If I understood corretcly what you want is to practice, maybe you can try this:
const randomStrings = ['gelato', 'frozen_yogurt', 'ice_cream'];
and then when you click on the Checkbox it will trigger onChange wich will be connected to handleGroupChange function and this would look something like:
const handleGroupChange = (isChecked) = {
if(isChecked){
//We create a random number between 0 and 2
const randomIndex = Math.floor(Math.random() * 2);
//We select an element from randomStrings array based on the random
index generated above
const randomStr = this.randomStrings[randomIndex];
//Looks if the randomStr already exist in existSelection
if nothing found it findIndex returns - 1 wich tell us
this item is not in the array so we push it.
const existInArr = this.existSelection.findIndex(randomStr);
if(existInArr === - 1){
existSelection.push(randomStr);
}
//I guess you also wanna feed a useState hook here.
setExistSelection(randomStr);
}
};
This should do the trick.
Oky, so wasted like 12 hours already on this. And I really need help.
I created a filter using an array via Map that returns some checkbox components
Component:
import { useEffect, useState } from "react"
interface Checkbox {
id: string | undefined,
name: string,
reg: any,
value: any,
label: string,
required?: boolean,
allChecked: boolean
}
const Checkbox = ({ id, name, reg, value, label, allChecked }: Checkbox) => {
const [checked, setChecked] = useState(false);
useEffect(() => {
setChecked(allChecked);
}, [allChecked])
return (
<>
<label key={id}>
<input type="checkbox"
{...reg}
id={id}
value={value}
name={name}
checked={checked}
onClick={() => {
setChecked(!checked)
}}
onChange={
() => { }
}
/>
{label}
</label>
</>
)
}
export default Checkbox;
Map:
dataValues.sort()
?
dataValues.map((e: any, i: number) => {
let slug = e.replace(' ', '-').toLowerCase();
return (
<div key={JSON.stringify(e)} id={JSON.stringify(e)}
>
<Checkbox id={slug}
name={title as string}
label={e as string}
value={e}
reg={{ ...register(title as string) }}
allChecked={allChecked}
/>
</div>
)
})
:
null
}
State that is just above the Map:
const [allChecked, setAllChecked] = useState<boolean>(false);
When I try to change the state on the parent and check or uncheck all of the checkboxes, nothing happens.
(the form works without a problem if I manually click on the checkboxes, but I cannot do this as I have some sections with over 40 values)
Sample of array:
dataValues = [
"Blugi",
"Bluza",
"Body",
"Bratara",
"Camasa",
"Cardigan",
"Ceas",
"Cercel",
"Colier",
"Fusta",
"Geanta Cross-body",
"Hanorac",
"Jacheta",
"Palton",
"Pantaloni",
"Pulover",
"Rochie",
"Rucsac",
"Sacou",
"Salopeta",
"Set-Accesorii",
"Top",
"Trench",
"Tricou",
"Vesta"
]
allChecked never changes (at least in the code shown here).
Here's the timeline:
The parent passes down a boolean prop allChecked. That's supposed to tell us if all the checkboxes are checked or not.
useEffect in Checkbox runs on mount, and allChecked is false because that's its default. useEffect then sets checked to allChecked's value, false. Which it already is, because its default is also false.
useEffect then listens for a change in allChecked via [allChecked] that never comes.
Clicking on any checkbox just toggles the state of that checkbox. allChecked's setter, setAllChecked, is never passed to the child or called from the parent.
What's the solution?
Somewhere, setAllChecked(true) needs to happen. Maybe a single checkbox with a label "Check all"?
Then, allChecked in Checkbox needs to be able to control the checkbox inputs. One implementation could be:
checked={checked || allChecked}
I managed to solve this.
There were 2 main problems:
Checkboxes were not rendering again so react-hook-form would see the old value, no matter what
I couldn't press clear all multiple times, because I was sending the allChecked = = false info multiple times, and the state wasn't exactly changing.
What I did was force a render by integrating the allChecked state as an object
interface checkedState {
checked: boolean,
render: boolean
}
So, whenever I send the state, I send it something like:
{
checked: true,
render: !allChecked.render
}
meaning that the new object is always new, no matter if I send the same information.
I am trying to use Semantic UI react for layout. These are the following sample code I am having trouble with onChange. I can check to click the toggle but resets everytime I refresh.
import {
Checkbox
} from 'semantic-ui-react'
onChangeInput(event) {
let name = event.target.name
let value = event.target.value
let talent = this.state.newTalentProfile
talent[name] = value
this.setState({
newTalentProfile: talent
})
}
<Select
name = "willing_to_relocate"
ref = "willing_to_relocate"
defaultValue = {this.props.talent.willing_to_relocate}
onChange = { this.onChangeInput.bind(this)} >
<Option value = ""label = "" / >
<Option value = "YES"label = "YES" / >
<Option value = "NO"label = "NO" / >
</Select>
the below code doesn't work, but the above one works when i make changes it saves it to database
<Checkbox toggle
name = "willing"
ref = "willing"
label = "Willin To Relocate"
onChange = {this.onChangeInput.bind(this)
}
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
onChangeCheckbox = (evt, data) => {
let checked = data.checked
console.log(checked)
}
<Checkbox toggle label='Running discounted price?'
onClick={(evt, data)=>this.onChangeCheckbox(evt, data)}
/>
This should do the trick.
Your code suggests you expect event.target to be a checkbox element, but it is not. If you examine the variable event.target within your browser, you'll notice that it points to a label element, not the checkbox itself. Therefore, I'm guessing that your event.target.value and event.target.label are evaluating to null, causing the rest of your code to not function the way you expect.
There's many ways to get around this. One way is to set a state variable for your component to represent the state of the checkbox:
class TheComponentThatContainsMyCheckbox extends Component {
state = {
textValue: null,
willingToRelocate: true
}
...
Then, you can create a handler that toggles this state when checked:
toggleCheckBox = () => {
const willingToRelocate = !(this.state.willingToRelocate);
this.setState({willingToRelocate});
// Note: if you have other logic here that depends on your newly updated state, you should add in a callback function to setState, since setState is asynchronous
}
Notice that I'm using arrow syntax here, which will automatically bind this for me. Then, hook it up to the onChange event of the Semantic UI Checkbox:
<Checkbox label='Willing to Relocate.' onChange={this.toggleCheckBox}/>
You can now use your this.willingToRelocate state to decide other application logic, passing it up or down using whatever state management pattern you like (state container/store like Redux, React Context, etc.).
I assume your this.onChangeInput handles database updates.
semantic ui react offers a prop checked for Checkbox.
You should have a state {checked: false} in your code, then this.setState whatever in componentDidMount after retrieving the database record.
Pass this.state.checked into checked prop of Checkbox.