0

I'm programatically validating an email and password inputs for simple login, here is the function that call other function that validate the email.

  handleLogin(event) {
    this.validateEmail();
    this.validatePassword();

    if (this.state.emailValid === 'error' || this.state.passwordValid === 'error') {
      alert('invalid form');
      return;
    };

    const email = ReactDOM.findDOMNode(this.refs.email).value;
    const password = ReactDOM.findDOMNode(this.refs.password).value;
    const creds = { email: email, password: password }
    this.props.onLoginClick(creds)
  }

Notice that first than all I'm calling the validateEmail() function which modifies the store that indicates if the input is correct, here's the validateEmail() source code:

  validateEmail() {
    const email = ReactDOM.findDOMNode(this.refs.email).value;
    let validEmail = /^.+([.%+-_]\w+)*@\w+([.-]\w+)*\.\w+([-.]\w+)*$/.test(email);

    if (!validEmail) {
      this.setState({
        emailValid: 'error'
      });
      return;
    }
    this.setState({
      emailValid: 'success'
    });
  }

But in the if statement the state.emailValid has not been yet updated, this is a delay in the state modifying, so the alert() is not displayed. How to get the updated state correctly?

Thanks

Walter Devia
  • 55
  • 1
  • 1
  • 7
  • Possible duplicate of [React: Delay in updating the state](https://stackoverflow.com/questions/36446355/react-delay-in-updating-the-state) – Catalyst Jul 19 '17 at 22:47
  • setState is asynchronous. Even though your validate functions are at the top set state does not occur until after everything else – EJ Mason Jul 19 '17 at 23:56
  • Check out this page from the react documentation about state and the single source of truth, controlled components: https://facebook.github.io/react/docs/forms.html – EJ Mason Jul 19 '17 at 23:57

1 Answers1

0

The thing to note here is that setState is asynchronous. It will not update the state until everything else that is synchronous in your handleLogin method has completed.

With React I like to use state as a single source of truth as often as I can. In the example above you have the html element as a source of truth and state. By changing your components to be controlled by the react state, you can validate your forms on each keystroke.

Forms and Controlled Components

Start by keeping the state of your input in state

class LoginForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      email: '',
      emailValid: true,
    };

    // we bind the function in case we want to 
    // control text in child component
    this.emailChange = this.handleEmailChange.bind(this);
  }

  emailChange(event) {
    this.setState({email: event.target.value});
  }

  render() {
    <textarea value={this.state.email} onChange={this.emailChange} /> 
  }
}

Now whenever you type the state of your html input is handled in react. This will enable you to more easily check its validity. We can do this by adding another method to our class:

class LoginForm extends React.Component {

  // ...all the stuff from above

  validateEmail() {
    let validEmail = /^.+([.%+-_]\w+)*@\w+([.-]\w+)*\.\w+([-.]\w+)*$/.test(email);

    if (!validEmail) {
      // Object.assign just ensures immutability
      this.setState(Object.assign({}, this.state, {
        emailValid: false
      }))
    } else {
      // If using babel, this is ensure immutable also
      this.setState({
        ...state,
        emailValid: true
      })
    }
  }

  // or....

  validateEmail() {
    let validEmail = /^.+([.%+-_]\w+)*@\w+([.-]\w+)*\.\w+([-.]\w+)*$/.test(email);
    this.setState({...state, emailValid: validEmail})
  }

  // ...render method
}

The validation will now occur on each keystroke. When you need to submit your form all you need to do is check the state if the data is valid and dont need to reference the dom. You can send the data from state.

EJ Mason
  • 2,000
  • 15
  • 15