Search code examples
reactjsreduxredux-form

Redux-Form: Change in FieldArray submits parent form


I'm stuck on an issue with redux-form. I have a form which is being populated with initialValues. Inside the top level form I have a FieldArray which renders a list of input fields. Each input field has buttons to remove it (via fields.remove(index)) or insert another input right after it (via fields.insert(index+1)). My problem is: when I call fields.insert() the new field gets inserted and registered correctly, but the parent form gets submitted, which it shouldn't. Same happens on fields.push() when the data array has at least one item. But it does not on e.g. fields.remove() or fields.removeAll(). I cannot figure out why/where the submittal occurs. I've spend hours digging through the source and playing around with the official FieldArray example which does work. I couldn't find the issue online so I guess I have a bug somewhere, since this is independent of the redux-form version in use (tried >=6).

Thanks for your help, here's the code.

Parent Form

import React from 'react';
import { Field, FieldArray, reduxForm } from 'redux-form';

import Row from 'muicss/lib/react/row';
import Col from 'muicss/lib/react/col';
import Button from 'muicss/lib/react/button';
import MaterialInput from '../material/material-input';
import MaterialSelect from '../material/material-select';
import MaterialFieldArray from '../material/material-fieldarray';

import Cover from './cover';
import CheckboxGroup from './checkbox-group';

const MovieDetailsEditForm = props => {

    const { handleSubmit, initialValues: { cover: { large: cover }, directors = [], actors = [] } } = props;

    const getFSKOptions = () => {
        return [
            {value: 0, label: 0},
            {value: 6, label: 6},
            {value: 12, label: 12},
            {value: 16, label: 16},
            {value: 18, label: 18}
        ];
    };

    const getFormats = () => {
        return [{value: 'DVD', label: 'DVD'}, {value: 'Blu-ray', label: 'Blu-Ray'}];
    }

    const getImages = () => props.initialValues.images.map( (image) => ({ value: image.large, checked: false }) );

    return (
        <Row>
            <form className="mui-form" onSubmit={handleSubmit(props.handleFormSubmit)}>
                <Col xs="12">
                    <Button type="submit" color="primary">Speichern</Button>
                </Col>
                <Col xs="12">
                    <Row>
                        <Col xs="12" md="6">
                            <Row>
                                <Col xs="12">
                                    <Field name="title" id="title" component={MaterialInput} label="Filmtitel" type="text" />
                                </Col>
                                <Col xs="6">
                                    <Field name="duration" id="duration" component={MaterialInput} label={props.initialValues.unit} type="text" />
                                </Col>
                                <Col xs="6">
                                    <Field
                                        name="format"
                                        id="format"
                                        options={getFormats()}
                                        component={MaterialSelect}
                                        label="Format"
                                        parse={(value, name) => value ? value : null}
                                    />
                                </Col>
                                <Col xs="12">
                                    <Field
                                        name="fsk"
                                        id="fsk"
                                        options={getFSKOptions()}
                                        component={MaterialSelect}
                                        label="FSK Einstufung"
                                        labelText="Freigegeben ab <<PLACEHOLDER>> Jahren"
                                        parse={(value, name) => value ? value : null}
                                    />
                                </Col>
                                { directors &&
                                    <Col xs="12">
                                        <h3>Regisseur{directors.length > 1 ? 'e' : ''}</h3>
                                        <FieldArray component={MaterialFieldArray} name="directors" />
                                    </Col>
                                }
                                { actors &&
                                    <Col xs="12">
                                        <h3>Cast</h3>
                                        <FieldArray component={MaterialFieldArray} name="actors" />
                                    </Col>
                                }
                            </Row>
                        </Col>
                        <Col xs="12" md="6" className="cover">
                            <Field {...props} name="cover" id="cover" component={Cover} />
                        </Col>
                    </Row>
                </Col>
                <Col xs="12">
                    <Field {...props} name="images" component={CheckboxGroup} valueProperty="large" />
                </Col>
            </form>
        </Row>
    );

}

export default reduxForm({
    form: 'MovieDetails',
    enableReinitialize: true
})(MovieDetailsEditForm);

material-fieldarray.js

import React from 'react';
import { Field } from 'redux-form';

import Row from 'muicss/lib/react/row';
import Col from 'muicss/lib/react/col';
import Button from 'muicss/lib/react/button';

import MaterialInput from '../material/material-input';

export default props => {
    const { fields } = props;

    const addEntry = index => fields.insert(index + 1, '');
    const removeEntry = index => fields.remove(index);

    const renderEntries = () => {
        return fields.map((field, index) => {
            return (<li key={index}>
                <Col xs="7" sm="8" md="7" lg="8">
                    <Field component={MaterialInput} name={`${field}`} id={`${field}`} />
                </Col>
                <Col xs="5" sm="4" md="5" lg="4" className="buttons">
                    <Button variant="fab" size="small" color="primary" onClick={() => addEntry(index)}>+</Button>
                    <Button variant="fab" size="small" color="danger" onClick={() => removeEntry(index)}>-</Button>
                </Col>
            </li>);
        })
    }

    return (
        fields.length &&
        <ul className="inputfield-list">
            { renderEntries() }
        </ul>
        || <Button onClick={() => fields.push('')}>Add</Button>
    )
}

Solution

  • OK, the problem was a very subtle one and has nothing to do with React or Redux-Form. I forgot to put type="button" on the buttons that add/remove items to the FieldArray.

    <Button variant="fab" size="small" color="primary" onClick={() => addEntry(index)}>+</Button>

    That's how HTML forms work.