Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redux Forms sometimes ends up with register/unregister infinite loop

I'm having trouble taming Redux Forms.

I have the following form and fields. Which seems to work if I simply add the correct data and submit, but if I try to cause validation errors and re-enter data a few times it sometimes ends up in an infinite loop.

The infinite loop is calling action @@redux-form/REGISTER_FIELD and action @@redux-form/UNREGISTER_FIELD repeatedly and eventually results in the following error.

Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

Can anyone help me to understand what might be causing this and what steps I can take to get it working please?

ContactPage.js

import React, {Component} from 'react';
import ContactForm from './contact-form';

class ContactPage extends Component {
    submit = values => {
        console.log(values);
    };

render() {
    return (
        <div>
            <ContactForm onSubmit={this.submit}/>
        </div>
    );

    }
}

export default ContactPage;

ContactForm.js

import React from 'react';
import {Field, reduxForm} from 'redux-form';
import {isEmail, isRequired, maxLength} from '../validation';
import {Input, Select, TextArea} from './form-elements';

let ContactForm = ({handleSubmit}) =>
    <form onSubmit={handleSubmit}>
        <div>
            <Field name="fullname"
                   label="Full Name"
                   component={Input}
                   type="text"
                   validate={[isRequired, maxLength(5)]}
            />
        </div>
        <div>
            <Field name="email" component={Input} type="email"
                   validate={[isRequired, maxLength(254), isEmail]}
                   classes={['email-field']}
            />
        </div>
        <div>
            <Field name="message" component={TextArea}
                   validate={[isRequired]}
            />
        </div>
        <button type="submit">Submit</button>
    </form>
;

ContactForm = reduxForm({
    form: 'contact'
})(ContactForm);

export default ContactForm;

FormElements.js

import React from 'react';

const Label = ({label, forName}) => <label htmlFor={forName} className="form-label">{label}</label>;

export const Input = ({input, label, type, classes = [], meta: {touched, error}}) => (
    <div className={['form-field', 'input', ...classes].join(' ')}>
        <Label label={label} forName={input.name}/>
        <div>
            <input {...input} type={type} placeholder={label}
                   className={['form-input', touched && error ? 'form-error' : ''].join(' ')}/>
            {touched && error && <p className='form-error'>{error}</p>}
        </div>
    </div>
);

export const TextArea = ({input, label, classes = [], meta: {touched, error}}) => (
    <div className={['form-field', 'text-area', ...classes].join(' ')}>
        <Label label={label} forName={input.name}/>
        <div>
            <textarea {...input} placeholder={label} className={[touched && error ? 'form-error' : ''].join(' ')}/>
            {touched && error && <p className='form-error'>{error}</p>}
        </div>
    </div>
);
like image 865
OrderAndChaos Avatar asked Jul 16 '18 18:07

OrderAndChaos


1 Answers

Don't do this:

validate={[isRequired, maxLength(5)]}

every time the form is rendered maxLength(5) will construct a new function, which will cause field to rerender (because this.props.validate !== nextProps.validate)

You can use specifically defined instances of parameterized validation rules:

const maxLength = max => value =>
    value && value.length > max ? `Must be ${max} characters or less` : undefined;
const maxLength15 = maxLength(15);

<Field
    name="username"
    type="text"
    component={renderField}
    label="Username"
    validate={[required, maxLength15]}
/>
like image 104
Igor Alemasow Avatar answered Nov 17 '22 01:11

Igor Alemasow