Khalil Gharbaoui
Khalil Gharbaoui

Reputation: 7032

How to disable form submit button until all input fields are filled?! ReactJS ES2015

Hi i found an answer to this for a single field form... but what if we have a form with multiple field?

this is fine for disabling it if you have 1 field but it does not work when you want to disable it based on many fields:

getInitialState() {
    return {email: ''}
  },
  handleChange(e) {
    this.setState({email: e.target.value})
  },
  render() {
    return <div>
      <input name="email" value={this.state.email} onChange={this.handleChange}/>
      <button type="button" disabled={!this.state.email}>Button</button>
    </div>
  }
})

Upvotes: 11

Views: 49459

Answers (6)

clxrity
clxrity

Reputation: 336

I figured it's worth mentioning, you can always check the actual validity of the input value as well (to make sure it's actually an email, etc.)

For instance, with <input type="email" />:

const MyComponent = () => {
    const [email, setEmail] = useState<string>();
    // ...
    
    return (
        <div>
             <input type="email" className="invalid:focus:ring-red-400" placeholder="[email protected]" 
                    onChange={(e) => {
                        e.preventDefault();
                        if (e.target.validity.valid) setSharedEmail(e.target.value);
                    }}
             />
             <button disabled={!email} className="disabled:cursor-not-allowed">
                  Set email
             </button>
        </div>
    );

}

Upvotes: 0

Selim Hassairi
Selim Hassairi

Reputation: 21

  export default function SignUpForm() {

    const [firstName, onChangeFirstName] = useState("");
    const [lastName, onChangeLastName] = useState("");
    const [phoneNumber, onChangePhoneNumber] = useState("");

    const areAllFieldsFilled = (firstName != "") && (lastName != "") && (phoneNumber != "")

    return (
      <Button
        title="SUBMIT"
        disabled={!areAllFieldsFilled}
        onPress={() => {
          signIn()
        }
        }
      />
    )
    }

Similar approach as Shafie Mukhre's!

Upvotes: 2

Mohamed Sajjadh
Mohamed Sajjadh

Reputation: 167

This might help. (credits - https://goshakkk.name/form-recipe-disable-submit-button-react/)

import React from "react";
import ReactDOM from "react-dom";

class SignUpForm extends React.Component {
  constructor() {
    super();
    this.state = {
      email: "",
      password: ""
    };
  }

  handleEmailChange = evt => {
    this.setState({ email: evt.target.value });
  };

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

  handleSubmit = () => {
    const { email, password } = this.state;
    alert(`Signed up with email: ${email} password: ${password}`);
  };

  render() {
    const { email, password } = this.state;
    const isEnabled = email.length > 0 && password.length > 0;
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type="text"
          placeholder="Enter email"
          value={this.state.email}
          onChange={this.handleEmailChange}
        />
        <input
          type="password"
          placeholder="Enter password"
          value={this.state.password}
          onChange={this.handlePasswordChange}
        />
        <button disabled={!isEnabled}>Sign up</button>
      </form>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<SignUpForm />, rootElement);

Upvotes: 2

Shafie Mukhre
Shafie Mukhre

Reputation: 469

This is how I'd do it by only rendering the normal button element if and only if all input fields are filled where all the states for my input elements are true. Else, it will render a disabled button.

Below is an example incorporating the useState hook and creating a component SubmitButton with the if statement.

import React, { useState } from 'react';

export function App() {
  const [firstname, setFirstname] = useState('');
  const [lastname, setLastname] = useState('');
  const [email, setEmail] = useState('');
    
  function SubmitButton(){
    if (firstname && lastname && email){
      return <button type="button">Button</button>
    } else {
      return <button type="button" disabled>Button</button>
    };
  };

  return (
    <div>
      <input value={email} onChange={ e => setEmail(e.target.value)}/>
      <input value={firstname} onChange={ e => setFirstname(e.target.value)}/>
      <input value={lastname} onChange={ e => setLastname(e.target.value)}/>
      <SubmitButton/>
    </div>
  );
};

Upvotes: 3

Andreyco
Andreyco

Reputation: 22862

I would take a little bit different way here...

Instead of setting submitDisabled in every single onChange handler I would hook into lifecycle method to listen to changes.
To be exact into componentWillUpdate(nextProps, nextState). This method is invoked before every change to component - either props change or state change. Here, you can validate your form data and set flag you need - all in one place.
Code example:

componentWillUpdate(nextProps, nextState) {
  nextState.invalidData = !(nextState.email && nextState.password);
},

Full working fiddle https://jsfiddle.net/4emdsb28/

Upvotes: 7

wintvelt
wintvelt

Reputation: 14101

Here is a basic setup for form validation:

getInitialState() {
    return {
      email: '',
      text: '',
      emailValid: false,         // valid flags for each field
      textValid: false, 
      submitDisabled: true       // separate flag for submit
    }
  },
  handleChangeEmail(e) {         // separate handler for each field
    let emailValid = e.target.value ? true : false;        // basic email validation
    let submitValid = this.state.textValid && emailvalid   // validate total form
    this.setState({
      email: e.target.value
      emailValid: emailValid, 
      submitDisabled: !submitValid
    })
  },
  handleChangeText(e) {         // separate handler for each field
    let textValid = e.target.value ? true : false;        // basic text validation
    let submitValid = this.state.emailValid && textvalid   // validate total form
    this.setState({
      text: '',
      textValid: textValid, 
      submitDisabled: !submitValid
    })
  },
  render() {
    return <div>
      <input name="email" value={this.state.email} onChange={this.handleChangeEmail}/>
      <input name="text" value={this.state.text} onChange={this.handleChangeText}/>
      <button type="button" disabled={this.state.submitDisabled}>Button</button>
    </div>
  }
})

In a more elaborate setup, you may want to put each input field in a separate component. And make the code more DRY (note the duplication in the change handlers).
There are also various solutions for react forms out there, like here.

Upvotes: 8

Related Questions