Reputation: 1654
Good evening, my guys, I'm facing a seemingly simple problem, and sorry for my bad English. The handleSubmit method is called when clicking on a save button, so the ternary conditionals check if the fields are within the validation, if not, it has its state changed to true, where error is rendered in the inputs.
Initial state
nameError: false,
cpfError: false,
emailError: false,
phoneError: false,
birthdayError: false,
addressError: false,
numberError: false,
districtError: false,
stateError: false,
cityError: false,
cepError: false,
Full Method Submit:
handleSubmit = () => {
this.state.name.length < 4
? this.setState({ nameError: true })
: this.setState({ nameError: false })
this.state.cpf.length < 14
? this.setState({ cpfError: true })
: this.setState({ cpfError: false })
this.state.email.length === 0
? this.setState({ emailError: true })
: this.setState({ emailError: false })
this.state.phone.length < 15
? this.setState({ phoneError: true })
: this.setState({ phoneError: false })
this.state.birthday.length < 10
? this.setState({ birthdayError: true })
: this.setState({ birthdayError: false })
this.state.address.length === 0
? this.setState({ addressError: true })
: this.setState({ addressError: false })
this.state.number.length === 0
? this.setState({ numberError: true })
: this.setState({ numberError: false })
this.state.district.length === 0
? this.setState({ districtError: true })
: this.setState({ districtError: false })
this.state.state.length === 0
? this.setState({ stateError: true })
: this.setState({ stateError: false })
this.state.city.length === 0
? this.setState({ cityError: true })
: this.setState({ cityError: false })
this.state.cep.length !== 9
? this.setState({ cepError: true })
: this.setState({ cepError: false })
if (
!this.state.nameError &&
!this.state.cpfError &&
!this.state.emailError &&
!this.state.phoneError &&
!this.state.birthdayError &&
!this.state.addressError &&
!this.state.numberError &&
!this.state.districtError &&
!this.state.stateError &&
!this.state.cityError &&
!this.state.cepError
) {
this.props.updateUser({
name: this.state.name,
cpf: this.state.cpf,
email: this.state.email,
phone: this.state.phone,
birthday: this.state.birthday,
address: this.state.address,
number: this.state.number,
district: this.state.district,
complement: this.state.complement,
state: this.state.state,
city: this.state.city,
cep: this.state.cep,
avatar: this.state.avatar,
})
}
}
The problem is in this section here.
Here the condition does not pick up the new state that was previously set, it just picks up the new state if I click the save button again. Where am I going wrong? Someone could share knowledge. This in a scenario where all fields are empty soon everyone will have their error state changed to true in ternary conditionals, but the state in if is still the old one, is this because the setstate and asynchronous?
if (
!this.state.nameError &&
!this.state.cpfError &&
!this.state.emailError &&
!this.state.phoneError &&
!this.state.birthdayError &&
!this.state.addressError &&
!this.state.numberError &&
!this.state.districtError &&
!this.state.stateError &&
!this.state.cityError &&
!this.state.cepError
) {
this.props.updateUser({
name: this.state.name,
cpf: this.state.cpf,
email: this.state.email,
phone: this.state.phone,
birthday: this.state.birthday,
address: this.state.address,
number: this.state.number,
district: this.state.district,
complement: this.state.complement,
state: this.state.state,
city: this.state.city,
cep: this.state.cep,
avatar: this.state.avatar,
})
}
Upvotes: 0
Views: 37
Reputation: 2222
The issue is that setState
is asynchronous but you are checking the state before it has finished updating. Rather than having multiple setState
calls for each of your checks, you could refactor it to a single call, and then do the update in the setState
callback, e.g.:
// Collect all of your error items
const nameError = this.state.name.length < 4;
const cpfError = this.state.cpf.length < 14;
...
// Set the state with your error values
this.setState({
nameError,
cpfError,
...
}, () => { // <-- The second parameter of setState is a callback called after it is finished
if (
!this.state.nameError &&
!this.state.cpfError &&
...
) {
this.props.updateUser({
name: this.state.name,
...
});
}
);
Upvotes: 1
Reputation: 15688
Yes, the logic you are creating with this.setState()
and this.props.updateUser()
are asynchronous. What you want is to call this.props.updateUser()
after you have updated your state. this.setState()
actually comes with a optional secondary argument that you can call after the state has finished updating, which is exactly what you need.
Let me help you out here by helping you reorganize some of your code. Here is the sandbox as well: https://codesandbox.io/s/zxpnk301pl
Also, you really want to avoid continuously updating your state upon each check of the input value. This can lead to really bad performance issues. Better to update your state in just one action.
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
state = {
name: '',
cpfr: '',
email: '',
phone: '',
birthday: '',
address: '',
number: '',
district: '',
state: '',
city: '',
cep: '',
errors: {},
}
handleOnChange = event => {
this.setState({
[event.target.name]: event.target.value,
})
}
validateAndSubmit = event => {
event.preventDefault()
const {
name,
cpfr,
email,
phone,
birthday,
address,
number,
district,
state,
city,
cep,
errors,
} = this.state
name.length < 4 ? (errors.name = true) : delete errors.name
cpfr.length < 14 ? (errors.cpfr = true) : delete errors.cpfr
email.length === 0 ? (errors.email = true) : delete errors.email
phone.length < 15 ? (errors.phone = true) : delete errors.phone
birthday.length < 10 ? (errors.birthday = true) : delete errors.birthday
address.length === 0 ? (errors.address = true) : delete errors.address
number.length === 0 ? (errors.number = true) : delete errors.number
district.length === 0 ? (errors.district = true): delete errors.district
state.length === 0 ? (errors.state = true) : delete errors.state
city.length === 0 ? (errors.city = true) : delete errors.city
cep.length !== 9 ? (errors.cep = true) : delete errors.cep
this.setState(
{
errors: errors,
},
() => {
//check if there are no errors
if(Object.keys(this.state.errors).length === 0){
this.props.updateUser({
name: this.state.name,
cpf: this.state.cpf,
email: this.state.email,
phone: this.state.phone,
birthday: this.state.birthday,
address: this.state.address,
number: this.state.number,
district: this.state.district,
complement: this.state.complement,
state: this.state.state,
city: this.state.city,
cep: this.state.cep,
avatar: this.state.avatar,
})
}
}
)
}
render() {
return (
<div>
<form onSubmit={this.validateAndSubmit}>
<input
name="name"
value={this.state.name}
onChange={this.handleOnChange}
/>
<input
name="cpfr"
value={this.state.cpfr}
onChange={this.handleOnChange}
/>
<input
name="email"
value={this.state.email}
onChange={this.handleOnChange}
/>
<input
name="phone"
value={this.state.phone}
onChange={this.handleOnChange}
/>
<input
name="birthday"
value={this.state.birthday}
onChange={this.handleOnChange}
/>
<input
name="address"
value={this.state.address}
onChange={this.handleOnChange}
/>
<input
name="number"
value={this.state.number}
onChange={this.handleOnChange}
/>
<input
name="district"
value={this.state.district}
onChange={this.handleOnChange}
/>
<input
name="state"
value={this.state.state}
onChange={this.handleOnChange}
/>
<input
name="city"
value={this.state.city}
onChange={this.handleOnChange}
/>
<input
name="cep"
value={this.state.cep}
onChange={this.handleOnChange}
/>
<button>Submit</button>
</form>
You have {Object.keys(this.state.errors).length} errors
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
Upvotes: 1