reactjsreact-state

Trouble with State Update in React Login Component on Input Change


I have this basic Login component with email and password fields that use a two-way binding approach. The issue I'm facing is that my state is not updated when the onChange event triggers on either inputs. What is causing this?

import { useState } from "react";

export default function Login() {
  const [ enteredValues, setEnteredValues ] = useState({
    email: {
      value: '',
      didEdit: false
    },
    password: {
      value: '',
      didEdit: false
    }
  })

  function handleSubmit(event) {
    event.preventDefault()

    console.log(`User email: ${enteredValues.email.value}`)
    console.log(`User password: ${enteredValues.password.value}`)
  }

  function handleInputChange(identifier, value) {
    console.log(`Changing ${identifier} to ${value}`);

    setEnteredValues(prevValues => ({
      ...prevValues,
      [identifier]: {
        value: value,
        ...prevValues[identifier]
      }
    }))

  }

  function handleInputBlur(identifier) {
    setEnteredValues(prevValues => ({
      ...prevValues,
      [identifier]: {
        ...prevValues[identifier],
        didEdit: true
      }
    }))
  }

  return (
    <form onSubmit={ handleSubmit }>
      <h2>Login</h2>

      <div className="control-row">
        <div className="control no-margin">
          <label htmlFor="email">Email</label>
          <input
            id="email"
            type="email"
            name="email"
            onBlur={ () => handleInputBlur('email') }
            onChange={(event) => handleInputChange('email', event.target.value)}
            value={enteredValues.email.value}
          />
        </div>

        <div className="control no-margin">
          <label htmlFor="password">Password</label>
          <input
            id="password"
            type="password"
            name="password"
            onChange={(event) => handleInputChange('password', event.target.value)}
            value={enteredValues.password.value}
          />
        </div>
      </div>

      <p className="form-actions">
        <button className="button button-flat">Reset</button>
        <button className="button">Login</button>
      </p>
    </form>
  );
}

Solution

  • You're resetting your state immediately after updating it:

    setEnteredValues(prevValues => ({
      ...prevValues,
      [identifier]: {
        value: value,
        ...prevValues[identifier] // This is overwriting the value change above
      }
    }))
    

    Reverse those two lines for:

    setEnteredValues(prevValues => ({
      ...prevValues,
      [identifier]: {
        ...prevValues[identifier],
        value: value,
      }
    }))