Trying to build a react component where I need to control checked status of checboxes and select options when change event occurs. But I don't know how it is possible to get value of the checked checkbox(es) and set the state.
We're using custom data-binding. On page load, we're assigning selected value of the select, with jQuery.
Programmatically changing value of the select must update matching check-boxes.
When user checks/unchecks a checkbox, corresponding value must be toggled on the select.
With jQuery I would loop trough check-boxes and build array with checked values then assign this value to the select on checkbox change. And when select change event is triggered, I would uncheck all check-boxes and check the ones matching selected items.
This is my simplified code.
state = {
items: [
{Key: 1, Value: "A"},
{Key: 29, Value: "Z"}
],
selected: [1, 29]
}
function onSelectChange(){
// Update checked checkboxes
}
function onCheckboxChange(){
// Update selected options
}
<div>
<select multiple onChange={onSelectChange} className="hidden">
{this.state.items.map((item, i) =>
<option value={item.Key}>{item.Value}</option>
)}
</select>
<div className="checkboxes">
{this.state.items.map((item, i) =>
<input
type="checkbox"
key={i}
checked={this.state.selected.indexOf(item.Key) >= 0}
onChange={onCheckboxChange} />
)}
</div>
</div>
You would use this.setState({}) inside the event handler to update the state of a component in React. This triggers a rerender in React which allows you to query the the updated state (this.state.selected).
Be advised that this.setState() expects an immutable object, so you should never change the previous, but always set a new state object!
Answer to comment:
For selectItem:
onSelectChange = event => this.setState({selected:event.target.value})
and for checkboxes (note the prevState):
onCheckboxChange = item => event => this.setState(({selected,...prevState})=> ({
...prevState,
selected: event.target.checked? selected.concat(item): selected.filter(it=> it!== item)
}))
and usage:
{this.state.items.map((item, i) =>
<input
type="checkbox"
key={i}
checked={this.state.selected.indexOf(item.Key) >= 0}
onChange={onCheckboxChange(item)} />
)}
This has the downside that it will create a new function on each rerender, so it's better to create a custom CheckboxItem and pass the item to it and use a handleClick.
onChange function give event where you could check whether the select box is being checked or not using this you can update your state accordingly.
function onCheckboxChange(e){
console.log("checked", e.target.checked);
// Then, on the basis of boolean you can update your state
}
Related
I have 2 checkboxes A and B. I want to check whether checkbox B is checked or not on change of checkbox A and vice versa. Also I want to change checkbox B on change of checkbox A. How can we achieve that in react.js.
You can create a state for both of them and change it accordingly.
This way you'll have access to it whenever needed.
Also, to avoid handling changes separately for every input, you can give each of them a name and then have a single dedicated function that changes the value of the checkbox based on it's name.
Example:
function App() {
const [state, setState] = useState({
firstCheckbox: false,
secondCheckbox: false,
})
const handleChange = (e) => {
setState(prev => ({
...prev,
[e.currentTarget.name]: e.currentTarget.checked,
}));
};
return (
<>
<input
name='firstCheckbox'
type='checkbox'
checked={state.firstCheckbox}
onChange={handleChange}
/>
<input
name='secondCheckbox'
type='checkbox'
checked={state.secondCheckbox}
onChange={handleChange}
/>
</>
)
}
Currently in this example, each checkbox relates to it's own state.
However, you can easily adjust the handleChange function based on your needs.
currently I have a signup form with 5 options but I'm trying to find a way to limit so the user can only select 2 options and in case the user selects a third option the first one would be unchecked, I had found a way of doing this in plain js but I haven't found a react way of doing it. This is what I have so far, would it be better to handle with plain js instead of react?
{iconsPool.map((src, index) => (
<Box className="test">
<input type="checkbox" className="iconsCheckbox" id={iconsPool.id} />
<label for={iconsPool.id}>
<img className="signupIcons" src={iconsPool[index].src} key={index} />
</label>
{console.log(iconsPool)}
</Box>
))}
This can be implemented with a state as an array with 2 elements.
Two items of the state Array will represent the index of selected items.
If an checkbox is clicked, that checkbox and the one clicked right before will be checked. (Therefore unchecking the one that was clicked even before that)
This can be done by pushing the index of newly clicked checkbox into the head of array, and removing the last item of the array.
When an checked checkbox is clicked again, (therefore it should be unchecked,) the index of the checkbox is searched from the state array, and removed by replacing that value with undefined
Below is code, as an example
...
const [checkedItems, setCheckedItems] = useState([undefined,undefined])
// When an Item is clicked
const onClickItem = (itemIndex:number, isSelected: boolean) =>{
if(isSelected){
setCheckedItems(c=>([itemIndex,c[0]]))
} else {
if(itemIndex === checkedItems[0]){
setCheckedItems(c=>([undefined,c[1]]))
} else if(itemIndex === checkedItems[1]){
setCheckedItems(c=>([c[0],undefined]))
}
}
}
I have implemented the datepicker and timepicker with add,delete buttons in each row. When I click on add, will add new row and delete will delete row.
I have the code link https://codesandbox.io/s/zen-water-tfyoz?fontsize=14&hidenavigation=1&theme=dark
But how to handle the state for multiple datepicker and timepicker,
When change the date, it doesnot reflect the change in field.
https://codesandbox.io/s/zen-water-tfyoz?fontsize=14&hidenavigation=1&theme=dark
So the problem is in your renderRowData function:
<td key={`tableview-td-${rowId}-${index}`}>
{column.dataFieldId === "pickdate" ? (
<DatePicker
locale="en-GB"
className="datepicker"
name={"pickdate_" + rowId}
onChange={e =>
this.handleDatePicker(
e,
"pickdate_" + rowId,
column.dataFieldId,
row
)
}
value={this.state.pickdate}
/>
)
For value u use this.state.pickdate, but when value changes you set it with:
handleDatePicker = (value, name, field, row) => {
this.props.handleInputChange(value, field, row);
console.log("data", value, "for", name);
this.setState({ [name]: value });
};
wich means that your state is now:
{
["pickdate_" + rowId]: value // where row is selected row
}
you need to change your datepicker to access value like this:
<td key={`tableview-td-${rowId}-${index}`}>
{column.dataFieldId === "pickdate" ? (
<DatePicker
locale="en-GB"
className="datepicker"
name={"pickdate_" + rowId}
onChange={e =>
this.handleDatePicker(
e,
"pickdate_" + rowId,
column.dataFieldId,
row
)
}
value={this.state["pickdate_" + rowId] || this.defaultPickDate} // this will take new selected value or default if there is none
/>
)
Working example: https://codesandbox.io/s/funny-fog-1v9o3
The reason that the date field's changes aren't reflected in the UI is that you've implemented it as a controlled component (you are setting the value of each DatePicker based on the corresponding value in the state and updating the state when you change the value, which effectively synchronizes the component with the state, more or less), whereas the TimePickers do change in the UI when a new time is chosen because they are implemented as uncontrolled components. Controlled components are often the best method, but your date component isn't updating on change because there are problems in your handleDatePicker function, as pointed out by Kaca992's answer.
If you don't know much about controlled vs uncontrolled components, see here.
As for how to handle the state for multiple DatePickers and TimePickers, personally I'd recommend that you store them as an array of rows in the state. For example, this would be a default state:
this.state = {
rows: [
{
date: new Date(),
start: moment(),
end: moment(),
}
]
}
Each row element in the array would correspond to a row in the table, and you can just use array.map to render each row as a component that contains the DatePicker, TimePickers, and buttons, and then just send the array index along with the new value to your onChange functions so that the correct row's changes can be reflected in the new state.
This would require a bit of a re-write of your DynamicDateTimePicker class, but the logic would be much simpler and more readable than how you currently have it structured.
I have a dropdown where and i run a function onChange. After onChange i am filtering the current React state. This works, and i am able to filter on a unique value. However, i am not able to reset the state to the previous original state on change after switching to another dropdown select item.
handleStateOnChange = e => {
let selectedWerkgever = e.target.value;
const list = this.state.opleidingen.filter(({ opleiding_werkgever }) =>
selectedWerkgever.includes(opleiding_werkgever)
);
this.setState({
opleidingen: list,
isResetButtonActive: true,
});
console.log('changed');
};
I am filtering on everything inside the array that includes "opleiding_werkgever". But how can i first revert back on change, and re-filter again?
Switching to another dropdown on the same component or in the components sharing same state does not automatically reset the state. Your first call on handleStateChange filters the state and it will remain so until that component is unmounted. You could decide to retain the original opleidingen and then use it to reset the opleidingen when needed.
{
opleidingen: list,
isResetButtonActive: true,
originalOpleidingen : list
}
I would store selectedWerkgever in state and use that to filter your drop down elements. Keep the original list intact.
So simplify your handleStateOnChange:
handleStateOnChange = e => {
this.setState({
selectedWerkgever: e.target.value,
isResetButtonActive: true
});
};
And use this to filter your dropdown options:
<select>
{this.state.opleidingen.filter(({ opleiding_werkgever }) =>
this.state.selectedWerkgever.includes(opleiding_werkgever)).map(item=>
<Option value={item} />}>)
}
...
This is my Creatable component:
function optionsForSelect(field) {
return field
.values
.map((fieldOption) => {
return {value: fieldOption, label: fieldOption};
});
}
function PatientSelectInput({field, options, value, onChange, disabled}) {
const className = field.id + '-select';
return (
<label className={cx('input-label', className)}>
<div className="label-text">{field.displayName}</div>
<Creatable
value={value}
onChange={(selectedValue) => onChange(selectedValue ? selectedValue.value : null)}
disabled={disabled}
onBlurResetsInput={false}
onCloseResetsInput={false}
options={options} />
</label>
);
}
It is a functional component. When it renders, I can create a new option but when I hit tab or enter or click on the automatically generated "Create Option..." the newly created option disappears. I just want the default behavior.
What am I missing?
Unfortunately this version of react-select mutates it's options prop.
This is very bad coding and is fixed in version 2: https://github.com/JedWatson/react-select/issues/2484
The issue you're experiencing is coming from the way you construct the options. Your function creates a new object on each render, replacing the array containing the option you just created.
If you can't update your version I suggest you save the output of optionsForSelect to this.state.options, then pass the state variable into react-select. The upshot of doing this is you still have the ability to mutate the state of your element and limits the impact of the mutate.