11

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>
  }
})
3
  • I don't know React, but I'd guess you could add an isSubmitAllowed to the state object and reference that within the button's disabled property. And then isSubmitAllowed would implement whatever logic you needed to test whether the form is complete. Commented Mar 14, 2016 at 1:35
  • Completely depends on the specific form in question. Nothing is stopping you from having a flag for every button. Commented Mar 14, 2016 at 2:02
  • IMO the simplest and most robust way of doing this, is calculating it inside the render method. For clarity, you may calculate e.g. const canSubmit = ... on a separate line before the return statement, and set disabled={!canSubmit} as a prop to the button. Commented Mar 14, 2016 at 12:26

6 Answers 6

8

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.

Sign up to request clarification or add additional context in comments.

Comments

7

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/

1 Comment

Nice setup! you probably mean componentWillUpdate() in the description as well. (componentWillMount() does not take any arguments) and you would probably need another lifecycle event for inititial validation.
3

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>
  );
};

Comments

2

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);

Comments

2
  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!

Comments

0

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>
    );

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.