I\'m making a primitive quiz app with 3 questions so far, all true or false. In my handleContinue
method there is a call to push the users input from a radio fo
Do not modify state directly! In general, try to avoid mutation.
Array.prototype.push()
mutates the array in-place. So essentially, when you push
to an array inside setState
, you mutate the original state by using push
. And since push
returns the new array length instead of the actual array, you're setting this.state.userAnswers
to a numerical value, and this is why you're getting Uncaught TypeError: this.state.userAnswers.push is not a function(…)
on the second run, because you can't push
to a number.
You need to use Array.prototype.concat() instead. It doesn't mutate the original array, and returns a new array with the new concatenated elements. This is what you want to do inside setState
. Your code should look something like this:
this.setState({
userAnswers: this.state.userAnswers.concat(this.state.value),
questionNumber: this.state.questionNumber + 1
}
Array.push does not returns the new array. try using
this.state.userAnswers.concat([this.state.value])
this will return new userAnswers array
References: array push and array concat
You should treat the state object as immutable, however you need to re-create the array so its pointing to a new object, set the new item, then reset the state.
handleContinue() {
var newState = this.state.userAnswers.slice();
newState.push(this.state.value);
this.setState({
//this push function throws error on 2nd go round
userAnswers: newState,
questionNumber: this.state.questionNumber + 1
//callback function for synchronicity
}, () => {
if (this.state.questionNumber > 3) {
this.props.changeHeader(this.state.userAnswers.toString())
this.props.unMount()
} else {
this.props.changeHeader("Question " + this.state.questionNumber)
}
})
console.log(this.state.userAnswers)
}
Another alternative to the above solution is to use .concat()
, since its returns a new array itself. Its equivalent to creating a new variable but is a much shorter code.
this.setState({
userAnswers: this.state.userAnswers.concat(this.state.value),
questionNumber: this.state.questionNumber + 1
}
The recommended approach in later React versions is to use an updater function when modifying states to prevent race conditions:
this.setState(prevState => ({
userAnswers: [...prevState.userAnswers, this.state.value]
}));
I have found a solution. This shoud work for splice and others too. Lets say that I have a state which is an array of cars:
this.state = {
cars: ['BMW','AUDI','mercedes']
};
this.addState = this.addState.bind(this);
Now, addState is the methnod that i will use to add new items to my array. This should look like this:
addState(){
let arr = this.state.cars;
arr.push('skoda');
this.setState({cars: arr});
}
I have found this solution thanks to duwalanise. All I had to do was to return the new array in order to push new items. I was facing this kind of issue for a lot of time. I will try more functions to see if it really works for all functions that normally won't. If anyone have a better idea how to achieve this with a cleaner code, please feel free to reply to my post.