ReactJS - using onBlur to validate email field

x86-64 picture x86-64 · Dec 11, 2018 · Viewed 12.2k times · Source

I have a MaterialUI Textfield for email which validates input when the user leaves the field. I make use of a function on onBlur for that purpose. I want to show an error message using helpertext.

The code for email field:

<TextField
    margin="dense"
    id="emailAddress"
    name="email"
    label="email"
    type="email"
    onChange={onChange}
    onBlur={validateEmail}
    value={email}
    error={validEmail}
    helperText={validEmail ? 'Please enter a valid Email' : ' '}
    fullWidth
 />

The validEmail variable is initialized inside the functional component as follows:

let validEmail = false;

And validateEmail is defined inside the same component as:

const validateEmail = () => {
    if (!email || invalidEmail(email)) {
      validEmail = true;
    } else {
      validEmail = false;
    }
};

But it doesn't work and error message doesn't get displayed. What am I potentially missing here? The condition that checks invalidEmail does get executed.

Answer

Kabbany picture Kabbany · Dec 11, 2018

I think the right way to do so is by using internal state of the component:

class EmailComponent extends React.Component {
  state = {
    validEmail: false,
    email: '',
  };

  onChange = (e) => {
    ...
    this.setState({
      email: e.target.value
    });
  }

  validateEmail = (e) => {
    const email = e.target.value;
    if (!email || invalidEmail(email)) {
      this.setState({
        validEmail: true,
      });
    } else {
      this.setState({
        validEmail: false,
      });
    }
  }

  render () {
    const { validEmail, email } = this.state;

    return (
      <TextField
        margin="dense"
        id="emailAddress"
        name="email"
        label="email"
        type="email"
        onChange={this.onChange}
        onBlur={this.validateEmail}
        value={email} //I think you should also set email as a state
        error={validEmail}
        helperText={validEmail ? 'Please enter a valid Email' : ' '}
        fullWidth
      />
    );
  }
}

Changing the value of a variable inside a stateless component won't let you achieve what you want since the component will not render again unless one of the props or the state is changed.

Edit: Use a parent component where you handle the state

class ParentComponent extends React.Component {
  state = {
    validEmail: false,
    email: '',
  };

  onChange = (e) => {
    this.setState({
      email: e.target.value
    });
  }

  validateEmail = (e) => {
    const email = e.target.value;
    if (!email || invalidEmail(email)) {
      this.setState({
        validEmail: true,
      });
    } else {
      this.setState({
        validEmail: false,
      });
    }
  }

  render () {
    const { validEmail, email } = this.state;

    return (
      <ChildComponent 
        email={email}
        validEmail={validEmail}
        onChange={this.onChange}
        validateEmail={this.validateEmail}
      />
    );
  }
}

const ChildComponent = (props) => (
  <TextField
    margin="dense"
    id="emailAddress"
    name="email"
    label="email"
    type="email"
    onChange={props.onChange}
    onBlur={props.validateEmail}
    value={props.email}
    error={props.validEmail}
    helperText={props.validEmail ? 'Please enter a valid Email' : ' '}
    fullWidth
  />
);