Reputation: 327
I have a react application in which there is a form with some fields along with validation. However, the validations are not working as I expect them to work. The code is below:
state = {
user: null,
errors: {}
}
onSubmitHandler = e => {
e.preventDefault();
//Check if first Name is returned empty
if (this.state.user.name.trim().length === 0) {
this.setState({
errors: {
...this.state.errors,
name: "This field is required"
}
});
}
//Check if last name is returned empty
if (this.state.user.surname.trim().length === 0) {
this.setState({
errors: {
...this.state.errors,
surname: "This field is required"
}
});
return;
}
}
Now when I do not provide both the input fields and leave them blank, I expect my state to be showing both error messages i.e errors: {name: "This field is required", surname: "This field is required"}
But I am only getting the recent error that is for a surname. So my result shows like errors: {surname: "This field is required"}
Why is it overriding the first error message? Any help would be appreciated. The code for input field is not shown as they are just input tags with onChange event handler.
Upvotes: 1
Views: 141
Reputation: 2804
That is because the second spread still uses the old value of this.state.errors as it has not changed yet. Both setStates may be (in your case : definately were) doing the state updates later, after the onSubmitHandler has executed.
Read also https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
Change the setState arguments to functions which use the latest state automatically:
onSubmitHandler = e => {
e.preventDefault();
//Check if first Name is returned empty
if (this.state.user.name.trim().length === 0) {
this.setState(state => ({
errors: {
...state.errors,
name: "This field is required"
}
}));
}
//Check if last name is returned empty
if (this.state.user.surname.trim().length === 0) {
this.setState(state => ({
errors: {
...state.errors,
surname: "This field is required"
}
}));
return;
}
}
Upvotes: 1
Reputation: 29292
Two this.setState
calls in the onSubmitHandler
will be batched together.
From the React Docs - setState():
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall
So when you do ...this.state.errors
, this.state.errors
in both if
blocks, refers to an empty object. As a result, you only see surname
key in the errors
object in the state.
You can fix this problem by copying the this.state.errors
object, add keys in the copied object and then pass that copied object to this.setState
.
onSubmitHandler = e => {
e.preventDefault();
// clone the "errors" object
const errors = { ...this.state.errors };
//Check if first Name is returned empty
if(this.state.user.name.trim().length === 0) {
errors.name = "This field is required";
}
//Check if last name is returned empty
if(this.state.user.surname.trim().length === 0) {
errors.surname = "This field is required";
}
this.setState({ errors });
}
Upvotes: 1