How to disable the login button when an api request is already in progress in React?

Viewed 346

I have this code to log the users in. Currently, when I hit login, the button remains active. What I want is to disable the button when login is clicked and the current request is in progress. I tried using state that is initially false, and when I click the button it becomes true. And when the loading is done it becomes false again. But it didn't really work or maybe I did it in the wrong way.

Here is the code:

import api from '../services/api'
import { useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { validateFormDataForLogin } from '../validate'

export default function LoginPage() {
  // form state
  const [formData, setFormData] = useState({
    'email': '',
    'password': '',
  })
  
  // using navigate
  const navigate = useNavigate()

  // function to handle the changes
  function handleChange(e) {
    // getting the required values
    const { name, value } = e.target

    // setting the state
    setFormData(prevFormData => ({
      ...prevFormData,
      [name]: value,
    }))
  }

  // function to handle the submit
  async function handleSubmit(e) {
    // preventing page reload
    e.preventDefault()

    // validating the data
    const errorMessage = validateFormDataForLogin(formData)

    // if there is an error message then show that error message
    if (errorMessage) {
      alert(errorMessage)
      return
    }

    // if there is no error message then try to login the user
    try {
      // making a request for login
      await api.post('/api/user/login', {
        email: formData.email,
        password: formData.password,
      })
      
      // redirect the user to the dashboard once logged in
      navigate('/dashboard', {
        replace: true, // so that when we hit back, we don't go to the login page again, and goes to the page before login
      })
    } catch (error) {
      // alerting the error
      if (error.response) {
        alert(error.response.data.message)
      }
    }
  }

  return (
    <div className='login'>
      <h1 className='login--title'>Login</h1>
      <div className='login--content'>
        <form className='login--form' onSubmit={handleSubmit}>
          <input type='text' className='login--form--input' placeholder='Email address' name='email' value={formData.email} onChange={handleChange} />
          <input type='password' className='login--form--input' placeholder='Password' name='password' value={formData.password} onChange={handleChange} />
          <p className='login--form--forgot_password'>Forgot Password</p>
          <input className='login--form--submit' type='submit' value='LOGIN' />
        </form>
        <div className='login--row'>
          <span className='login--row--text'>Don't have an account?</span>
          <Link to='/register' className='login--row--link'>Register</Link>
        </div>
      </div>
    </div>
  )
}

Any help will be greatly appreciated.

2 Answers

You can keep track of loading state in a separate state :

const [isLoading, setIsLoading] = useState(false)

async function handleSubmit(e) {
  setIsLoading(true)
  /* ... */

  try {
    // making a request for login
    await api.post('/api/user/login', {
      email: formData.email,
      password: formData.password,
    })
    setIsLoading(false)
    /* ... */
     
  } catch (error) {
    setIsLoading(false)
    /* ... */
  }
}

Then, disable the submit button if isLoading is true :

<input type="submit" disabled={isLoading} />

Psuedo code

  1. You can use a state variable to store disable / enable of the button. default is false. lets say variable is isDisabled. Like const [isDisabled, setIsDisabled] = useState(false)

  2. Use that variable in conditional validation on the button like

    <input className='login--form--submit' type='submit' value='LOGIN' disabled={isDisabled}

  3. You need to keep the state of the variable updated while making your API request / on success or on failure

Related