Stephanie Gredell
Stephanie Gredell

Reputation: 168

Why do my error messages not update when a field is filled out? (React)

This seems like a simple thing to do but after much fiddling, I can't figure out what's wrong. I'm a noob to React so forgive me.

I have a form for logging in. Like most login forms, it's asking for a username and password. Then it has a button to submit. My understanding is that a component will re-render if the state is changed. I have onchange events on each input field that updates the state. So if the field is empty, I press the button to submit, I would expect that the error will show. If I fill in a field, I would expect the error message to go away because the state changed. Am I misunderstanding?

Here is my event handler:

handleLogin(event) {
    event.preventDefault();
    if (this.state.username == '') {
      this.setState({usernameError: "Need to enter a username"})
      return;
    } 
    if (this.state.password == '') {
      this.setState({passwordError: "Need to enter a password"})
      return;
    }
  }

And the form:

render() {
    return(
      <form className="login-form">
        <h1 className="login-form__header"><FontAwesomeIcon icon="key" className="registration-form__icon"/><i className="fal fa-route-highway"></i>Log Into Your Account</h1>
        <input type="text" name="username" placeholder="Username" className="login-form__input" onChange={(event,newValue) => this.setState({username:newValue})}/>
        {this.state.usernameError &&
          <p class="login-form__error"><FontAwesomeIcon icon="times-circle"/> {this.state.usernameError}</p>
        }

        <input type="password" name="password" placeholder="Password" className="login-form__input" onChange={(event,newValue) => this.setState({password:newValue})}/>
        {this.state.passwordError &&
          <p class="login-form__error"><FontAwesomeIcon icon="times-circle"/> {this.state.passwordError}</p>
        }

        <button className="login-form__button" onClick={this.handleLogin}>Log Into Your Account</button>
      </form>
    );
  }

Upvotes: 0

Views: 1587

Answers (4)

Cat_Enthusiast
Cat_Enthusiast

Reputation: 15688

Right, but you never configured any logic to clear the errors if the field is not empty. Currently, there isnt any logic set-up to turn usernameError and passwordError back to an empty-string or null value.

You might be under the impression that the state is cleared when you re-render but that is not the case. The state-object prior to the re-render still persists, only changing the key-value pair(s) you last updated within this.setState().

Try this:

handleLogin(event) {
    event.preventDefault();
    const { username, password } = this.state
    this.setState({
      ...this.state,
      usernameError: username.length > 0 ? "" : "Need to enter a username",
      passwordError: password.length > 0 ? "" : "Need to enter a password"
   })
}

Here's a working sandbox with a sligtly modified version of your code. (I removed the FontAwesomeIcons). https://codesandbox.io/s/cool-meninsky-y9r4y

Upvotes: 2

techipank
techipank

Reputation: 430

As mentioned by @Satyaki onChange={(event,newValue) => this.setState({username:newValue})} is not a good option you should define separate method for this, alternatively you can change your code to as follows:

onChange={(event,newValue) => this.setState({username:newValue})}, () => {})

This will make sure to complete the lifecycle of setState.

Upvotes: 0

Jebasuthan
Jebasuthan

Reputation: 5604

Hi you can try the below code

function validate(email, username, password) {
 // true means invalid, so our conditions got reversed
 return {
   username: username.length === 0, //true if username is empty
   password: password.length === 0, //true if password is empty
 };
}

class LoginForm extends React.Component {
 constructor() {
  super();
  this.state = {
    username: '',
    password: '',

    touched: {
      username: false,
      password: false,
    },
  };
}

handleUsernameChange = (evt) => {
  this.setState({ username: evt.target.value });
}

handlePasswordChange = (evt) => {
  this.setState({ password: evt.target.value });
}

handleBlur = (field) => (evt) => {
  this.setState({
    touched: { ...this.state.touched, [field]: true },
  });
}

handleSubmit = (evt) => {
  if (!this.canBeSubmitted()) {
    evt.preventDefault();
    return;
  }
  const { username, password } = this.state;
  alert(`Logined: ${email} password: ${password}`);
}

canBeSubmitted() {
  const errors = validate(this.state.username, this.state.password);
  const isDisabled = Object.keys(errors).some(x => errors[x]);
  return !isDisabled;
}

render() {
  const errors = validate(this.state.username, this.state.password);
  const isDisabled = Object.keys(errors).some(x => errors[x]);

  const shouldMarkError = (field) => {
    const hasError = errors[field];
    const shouldShow = this.state.touched[field];
    return hasError ? shouldShow : false;
  };

  return (
    <form className="login-form" onSubmit={this.handleSubmit}>
      <h1 className="login-form__header">
        <FontAwesomeIcon icon="key" className="registration-form__icon"/>
        <i className="fal fa-route-highway"></i>Log Into Your Account
      </h1>
      <input 
        className={shouldMarkError('username') ? "error" : ""}
        type="text" name="username" placeholder="Username" 
        value={this.state.username}
        onBlur={this.handleBlur('username')
        onChange={this.handleUsernameChange} />
          <p className={shouldMarkError('username') ? "error" : "hidden"}>
            <FontAwesomeIcon icon="times-circle"/> Need to enter a username
          </p>       

      <input type="password" name="password" placeholder="Password" 
        className={shouldMarkError('password') ? "error" : ""}
        value={this.state.password}
        onChange={this.handlePasswordChange}
        onBlur={this.handleBlur('password')} />

        <p className={shouldMarkError('password') ? "error" : "hidden"}>
          <FontAwesomeIcon icon="times-circle"/> Need to enter a password
        </p>

        <button disabled={isDisabled} className="login-form__button" 
        onClick={this.handleLogin}>Log Into Your Account</button>
    </form>
  )
}
}

Hope the code could helpful for you. You can also refer the Demo Validation for Signup

Upvotes: 0

Satyaki
Satyaki

Reputation: 751

First of all, onChange={(event,newValue) => this.setState({username:newValue})} this is not a good approach, as it will create a new function on every render. So I would suggest to create a dedicated function like -

 handleInputChange(event) {
    const target = event.target;
    const value = target.value;
    const name = target.name;

 // Make sure the name is as per with your html username password and update the state accordingly

    this.setState({
      [name]: value
    });
  }

Do remember to reset the usernameError and passwordError properties of your state on each related onChange event. Otherwise the error message will persist on HTML.

Upvotes: 0

Related Questions