redux-form: How to disable submit button if at least one Field is not valid?
Asked Answered
T

7

17

I'm rendering the below simple form using redux-form and it's working nicely. Now, I'd like to have the submit button disabled in one more situation: If any of the Field's has an error (i.e. it's meta.error is set).

From lokking into the docs, I suppose it is not possible for the surrounding <form> to know if its <Field> components have an error. Maybe anyone has an idea, how to solve it as easy as using disabled={hasErrors || submitting || pristine}

const EditBlogEntryForm = ({ onSubmit, reset, handleSubmit,
                         pristine, submitting, ...rest }) => {
    console.log('rest: ', rest);
    return (
        <form onSubmit={handleSubmit(onSubmit)}>
            <div className="form-group">
                <Field name="title"
                    type="text"
                    component={renderField}
                    label="Titel"
                    className="form-control"
                    placeholder="Titel eingeben..." />
            </div>
            <div className="form-group">
                <Field name="text"
                    component={renderTextArea}
                    label="Text"
                    className="form-control"
                    placeholder="Textinhalt eingeben..." />
            </div>  
            <div className="form-group">
                <Field name="image"
                    type="text"
                    component={renderField}
                    label="Bild-URL:"
                    className="form-control"
                    placeholder="Bildadresse eingeben..." />
            </div>  
            <div>
                <button type="submit" className="btn btn-default"
                    disabled={submitting || pristine}>
                    Blogeintrag speichern
                </button>
                <button type="button" className="btn btn-default"
                    disabled={pristine || submitting}
                    onClick={reset}>
                    Formular leeren
                </button>
            </div>
        </form>
    );
};
Teacake answered 3/5, 2017 at 19:33 Comment(1)
what you can do is just add in your own variable and put it in the state like Errors. Once that value is false then you can hit the submit buttonLinoleum
B
27

Don't abuse state you need just using this.props for each setState Component one more time will be render

const {invalid} = this.props

return(
<button type="submit" className="btn btn-default"
     disabled={invalid|| submitting || pristine}>
     Blogeintrag speichern
 </button>)

More Document: https://redux-form.com/6.0.0-alpha.4/docs/api/props.md/

Biogen answered 3/12, 2017 at 10:40 Comment(0)
S
3

Redux forms already passes lots of properties into the form. One is invalid. That's what I am using to determine if any of the field validations failed and then disable submit.

https://redux-form.com/6.0.0-alpha.4/docs/api/props.md/

Sludge answered 12/9, 2018 at 22:39 Comment(0)
L
2

What you should be able to do is just have a variables called Errors that will be true once your api call comes back with an error

 constructor(super) {
      this.state = {
         errors: false,
      }
 }

 componentWillReceiveProps(nextProps) {
     const that = this;
     if (nextProps.errors) {
        that.setState({errors: true})
     }    
 }

 <button type="submit" className="btn btn-default"
     disabled={this.state.errors || submitting || pristine}>
     Blogeintrag speichern
 </button>
Linoleum answered 3/5, 2017 at 19:41 Comment(1)
as mentioned by @masoud-soroush, better not abuse the state. See his solution.Petuu
T
0

Alastair pointed me into the correct direction (Thanks for that!). I guess this is one of the cases where a local UI-related state is actually very useful. So I refactored the SFC into a react class. That classes' constructor and componentWillReceiveProps look like this:

constructor(props) {
    super(props);
    this.state = {
        errors: false
    };
}

componentWillReceiveProps(nextProps) {
    if (nextProps.invalid) {
        this.setState({errors: true});
    } else {
        this.setState({errors: false});
    }
}

Now using this.state.errors to have the button disabled is working perfectly. As you can see, I had to use the invalid prop form redux-form, because its error prop was always undefined and not to forget to set it true again, if the form is valid. Furthermore I don't know, why you copied the this reference into that in your answer. It wouldn't change any behaviour as it's still pointing to the same object.

Teacake answered 3/5, 2017 at 20:32 Comment(0)
H
0

If you are using the react-redux-form library you can eventually use the onUpdate event on the form to capture the validity state of the form and like this you can change some internal state variable which can be used to deactivate a button. Here is some sample code to demonstrate what you can do:

import React, { Component } from 'react';
import { Button, Modal, ModalHeader, ModalBody, Row, Label, Col } from 'reactstrap';
import { Control, LocalForm, Errors } from 'react-redux-form';

const required = (val) => val && val.length;

const maxLength = (len) => (val) => !(val) || (val.length <= len);

const minLength = (len) => (val) => (val) && (val.length >= len);

class CommentForm extends Component {

    constructor(props) {
        super(props);
        this.state = {
            isModalOpen: false,
            isFormInValid: true
        };
        this.toggleModal = this.toggleModal.bind(this);
    }

    toggleModal() {
        this.setState({
            isModalOpen: !this.state.isModalOpen
        })
    }

    handleSubmit(values) {
        console.log("Current state is: " + JSON.stringify(values));
        alert(JSON.stringify(values));
    }

    handleUpdate(form) {
        this.setState({ isFormInValid: !form['$form'].valid });
    }

    render() {
        return (
            <>
                <Button outline onClick={this.toggleModal}>
                    <span className="fa fa-pencil fa-lg"></span> Submit Comment
                </Button>
                <Modal isOpen={this.state.isModalOpen} toggle={this.toggleModal}>
                    <ModalHeader toggle={this.toggleModal}>Submit Comment</ModalHeader>
                    <ModalBody>
                        <LocalForm
                            onUpdate={(form) => this.handleUpdate(form)}
                            onSubmit={(values) => this.handleSubmit(values)}>
                            <Row className="form-group">
                                <Label htmlFor="author" md={12}>Your Name</Label>
                                <Col md={12}>
                                    <Control.text model=".author" id="author" name="author"
                                        placeholder="First Name" className="form-control"
                                        validators={{ required, minLength: minLength(3), maxLength: maxLength(15) }} />
                                    <Errors className="text-danger" model=".author" show="touched"
                                        messages={{ required: 'Required ', minLength: 'Must be greater than 2 characters', maxLength: 'Must be 15 characters or less' }} />
                                </Col>
                            </Row>
                            <Row className="form-group">
                                <Col md={12}>
                                    <Button type="submit" color="primary" disabled={this.state.isFormInValid}>Submit</Button>
                                </Col>
                            </Row>
                        </LocalForm>
                    </ModalBody>
                </Modal>
            </>
        );
    }
}
Hyla answered 25/5, 2019 at 11:22 Comment(0)
R
0

This is an update for Masoud Soroush's answer.

Checking the invalid prop only is enough.

const {invalid} = this.props

return(
<button type="submit" className="btn btn-default"
     disabled={invalid}>
     Blogeintrag speichern
</button>

Note: Use the submitting prop only if you have passed an onSubmit function that returns a promise. It will be true until the promise is resolved or rejected.

Rom answered 22/2, 2020 at 14:54 Comment(0)
D
-2

A possibility is to use validate field. You need to defined a function :

const required = value => (value ? undefined : 'Required')

And use this function in your field :

           <div className="form-group">
                <Field name="title"
                    type="text"
                    component={renderField}
                    label="Titel"
                    className="form-control"
                    placeholder="Titel eingeben..."
                    validate={[required]}
                />
            </div>
Deoxyribose answered 7/2, 2019 at 16:51 Comment(1)
This suggestion is taken from the docs and does not provide a complete solution.Unshaped

© 2022 - 2024 — McMap. All rights reserved.