Push checkbox value to array on state onChange
Solution 1
handleDaySelect(event){
let day_list = this.state.days;
let check = event.target.checked;
let checked_day = event.target.value;
if(check){
this.setState({
days: [...this.state.days, checked_day]
})
}else{
var index = day_list.indexOf(checked_day);
if (index > -1) {
day_list.splice(index, 1);
this.setState({
days: day_list
})
}
}
}
Hope this helps.
Solution 2
You need to check if the value is previously there, if not you have to add it else remove it. Also you should make sure that you are writing
this.state = {
days:[]
}
in constructor like
constructor() {
super();
this.state = {
days:[]
}
}
or if as a class Property you would write
state = {
days:[]
}
Also don't forget to bind
your handleDaySelect
function .
Snippet:
handleDaySelect = (event) => {
var dayArr = [...this.state.days];
const value = event.target.value
const index = dayArr.findIndex(day => day === value);
if(index > -1) {
dayArr = [...dayArr.slice(0, index), ...dayArr.slice(index + 1)]
} else {
dayArr.push(value);
}
this.setState({days: dayArr});
}
Solution 3
instead of defining a dayArr
in the handleDaySelect
I've declared it outside so that newly selected days can be pushed into it.Then it can be set as the new state of the app on every click (More previously on every day selection). Although you're rewriting the state every single click obviously but you'll not be losing your previous selected days.
this.state = {
days:[]
}
let dayArr;
handleDaySelect (event) {
/*now we should check if the selected days
already exist so that we don't double entry it*/
if (this.state.days.indexOf(event.target.value) < 1) {
dayArr.push(event.target.value)
}
this.setState({
days: dayArr
})
}
I hope it helps you, it would have been better if you shared your code on js fiddle or somewhere similar.
Thank you :)
Solution 4
The Problem
setState
is asynchronous.
So, when you're setting state based on a previous value, you should use functional setState
.
(for information on why, here's a good read)
Based on your description, it's also possible that you're either setting up state
in the wrong place or you're not bind
ing your function handleDaySelect
.
Naive Solution
class DaySelector extends Component {
// state as a static property. You can also use `getInitialState` or set `this.state` in your constructor.
state = {
days: []
}
// use an arrow function or `bind(this)` in the constructor
handleSelect = e => {
// grab the checkbox value since functional setState is async and won't have access to `e` without calling persist
const { value } = e.target
// functional setState because we're building off of the previous state
this.setState(prevState => ({
// copy over any other values from state
...prevState,
days: [
...prevState.days,
value
]
})
}
}
However, in order to make this work, you will need to handle the deselect differently than select. So, your onChange should check whether it needs to append the new value or remove an existing value. This can be a bit tedious.
A Better Alternative
You can make your life a bit easier by just storing a map of day->boolean. (here's another question that addresses the same problem)
const DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
class DaySelector extends Component {
state = {
days: DAYS.reduce(
(state, day) => ({ ...state, [day]: false }),
{}
)
}
handleSelect = e => {
const { name, checked } = e.target
this.setState(prevState => ({
...prevState,
days: {
...prevState.days,
[name]: checked
}
})
}
render() {
return (
// Using Fragments from React 16.2, but divs are fine too
<>
{
Object.keys(this.state.days).map(day => (
<Fragment key={day}>
<input
type="checkbox"
name={day}
checked={this.state.days[day]}
onChange={this.handleSelect}
/>
{day}
</Fragment>
))
}
</>
)
}
}
Then, to get the list of selected days, you can just do:
Object.keys(this.state.days).filter(day => this.state.days[day])
(for more info on my use of fragments, see this question and the documentation)
Admin
Updated on June 13, 2022Comments
-
Admin almost 2 years
I want to allow users to select days of the week from checkboxes. When a checkbox is checked, the value of that checkbox should update this.state.days. Instead of adding to the array, it's simply overwriting this.state.days. Tried using simple spread operator, but I then get an error indicating that react cant convert null into object - this even happens when I set days to [1,2] so that it isn't undefined at the start. See change function below
this.state = { days:[] } handleDaySelect (event) { if (this.state.days) { var dayArr = [...this.state.days] } else { var dayArr = []; } dayArr.push(event.target.value) this.setState({ days: dayArr }) }
Render function:
render() { const daysOptions = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"].map((cur, ind) => { return ( <div key={ind} className="checks" > <label> <input type="checkbox" name={cur} value={cur} onChange={this.handleDaySelect} />{cur} </label> </div> ) }) return ( <div id="newDeal" className="formContainer" > <div className="checkBoxContainer" > {daysOptions} </div> </div> ) }
-
Admin about 6 yearsproblem with this, is react is not recognizing this.state.days as a defined array. So [...this.state.days] results in an error: "Cannot convert null to object"
-
Chaim Friedman about 6 yearsthat sounds like you forgot to bind your
handleDaySelect
method -
Admin about 6 years@ChaimFriedman thought that might have been it, but it is indeed bound.
-
Shubham Khatri about 6 years@ConnorGRoane, First of all check if state is defined properly. Second, check the method is properly binded. Check the update.
-
Alok Ranjan almost 4 yearsNice approach to consider.