formio / react

JSON powered forms for React.js
https://form.io
MIT License
306 stars 272 forks source link

[BUG] <Form /> onChange event triggers infinitely with pre-populated submission data #414

Closed namti closed 1 year ago

namti commented 2 years ago

Environment

Please provide as many details as you can:

Steps to Reproduce

  1. Open console inspector,
  2. select one of radio buttons, or
  3. click on the "Force Update" button,
  4. check the console,
  5. select any datepicker, switch between months, the datepicker is reset due to infinite re-rendering

Expected behavior

  1. onChange shall only be triggered after submission data is changed
  2. datepickers should be able to choose the year and month

Observed behavior

If submission and onChange event are put together, the <Form /> creates infinite loop. It re-renders all elements and slows down the performance

Example

If possible, please provide a screenshot, live example (via JSFiddle or similar), and/or example code to help demonstrate the issue.

For code or form JSON, please enclose in a code block:

import React from "react";
import { form_json } from "./form_json";
import { Form } from "@formio/react";
// import { Form } from "react-formio";

class EditForm extends React.Component {
    constructor(){
        super();
        this.state = {
            submission: {
                "radio": "yes",
                "dateTime": "2021-11-10T12:00:00+13:00"
            },
        };
    }

    onSubmissionChange(submission){
        console.log('changed', submission);
        this.setState({
            submission: submission.data,
        });
    }

    render() {
        return (
            <div className="container">
                <div className="row">
                    <div className="col">

                        { console.log('render') }
                        <a className="btn btn-sm btn-primary" onClick={ () => this.forceUpdate() }>Force Update</a>

                        <Form 
                            form={form_json} 
                            onChange={ submission => this.onSubmissionChange(submission) } 
                            submission={{ data: this.state.submission }} 
                            options={{
                                noDefaults: false,
                            }}
                        />
                    </div>
                </div>
            </div>
        );
    }
}

export default EditForm;

form.json

export const form_json = {
    components: [
        {
            label: "Radio",
            labelPosition: "top",
            optionsLabelPosition: "right",
            description: "",
            tooltip: "",
            customClass: "",
            tabindex: "",
            inline: false,
            hidden: false,
            hideLabel: false,
            autofocus: false,
            disabled: false,
            tableView: false,
            modalEdit: false,
            values: [
                { label: "Yes", value: "yes", shortcut: "" },
                { label: "No", value: "no", shortcut: "" }
            ],
            dataType: "",
            persistent: true,
            protected: false,
            dbIndex: false,
            encrypted: false,
            redrawOn: "",
            clearOnHide: true,
            customDefaultValue: "",
            calculateValue: "",
            calculateServer: false,
            allowCalculateOverride: false,
            validate: {
                required: false,
                customMessage: "",
                custom: "",
                customPrivate: false,
                json: "",
                strictDateValidation: false,
                multiple: false,
                unique: false
            },
            errorLabel: "",
            key: "radio",
            tags: [],
            properties: [],
            conditional: { show: null, when: null, eq: "", json: "" },
            customConditional: "",
            logic: [],
            attributes: [],
            overlay: {
                style: "",
                page: "",
                left: "",
                top: "",
                width: "",
                height: ""
            },
            type: "radio",
            input: true,
            placeholder: "",
            prefix: "",
            suffix: "",
            multiple: false,
            unique: false,
            refreshOn: "",
            widget: null,
            validateOn: "change",
            showCharCount: false,
            showWordCount: false,
            allowMultipleMasks: false,
            inputType: "radio",
            fieldSet: false,
            id: "eobgfs",
            defaultValue: ""
        },
        {
            label: "Date / Time",
            labelPosition: "top",
            displayInTimezone: "viewer",
            useLocaleSettings: false,
            allowInput: true,
            format: "dd/MM/yyyy hh:mm a",
            placeholder: "",
            description: "",
            tooltip: "",
            customClass: "",
            tabindex: "",
            hidden: false,
            hideLabel: false,
            autofocus: false,
            disabled: false,
            tableView: false,
            modalEdit: false,
            enableDate: true,
            enableMinDateInput: false,
            datePicker: {
                minDate: null,
                maxDate: null,
                disable: "",
                disableFunction: "",
                disableWeekends: false,
                disableWeekdays: false,
                showWeeks: true,
                startingDay: 0,
                initDate: "",
                minMode: "day",
                maxMode: "year",
                yearRows: 4,
                yearColumns: 5
            },
            enableMaxDateInput: false,
            enableTime: true,
            timePicker: {
                showMeridian: true,
                hourStep: 1,
                minuteStep: 1,
                readonlyInput: false,
                mousewheel: true,
                arrowkeys: true
            },
            multiple: false,
            defaultValue: "",
            defaultDate: "",
            customOptions: [],
            persistent: true,
            protected: false,
            dbIndex: false,
            encrypted: false,
            redrawOn: "",
            clearOnHide: true,
            customDefaultValue: "",
            calculateValue: "",
            calculateServer: false,
            allowCalculateOverride: false,
            validate: {
                required: false,
                customMessage: "",
                custom: "",
                customPrivate: false,
                json: "",
                strictDateValidation: false,
                multiple: false,
                unique: false
            },
            unique: false,
            validateOn: "change",
            errorLabel: "",
            key: "dateTime",
            tags: [],
            properties: [],
            conditional: { show: true, when: "radio", eq: "yes", json: "" },
            customConditional: "",
            logic: [],
            attributes: [],
            overlay: {
                style: "",
                page: "",
                left: "",
                top: "",
                width: "",
                height: ""
            },
            type: "datetime",
            timezone: "",
            input: true,
            widget: {
                type: "calendar",
                displayInTimezone: "viewer",
                language: "en",
                useLocaleSettings: false,
                allowInput: true,
                mode: "single",
                enableTime: true,
                noCalendar: false,
                format: "dd/MM/yyyy hh:mm a",
                hourIncrement: 1,
                minuteIncrement: 1,
                time_24hr: false,
                minDate: null,
                disabledDates: "",
                disableWeekends: false,
                disableWeekdays: false,
                disableFunction: "",
                maxDate: null
            },
            prefix: "",
            suffix: "",
            refreshOn: "",
            showCharCount: false,
            showWordCount: false,
            allowMultipleMasks: false,
            datepickerMode: "day",
            id: "e527s4g"
        },
        {
            title: "Panel",
            theme: "default",
            tooltip: "",
            customClass: "",
            collapsible: false,
            hidden: false,
            disabled: false,
            tableView: false,
            modalEdit: false,
            key: "panel",
            tags: [],
            properties: [],
            customConditional: "show = data.radio === 'no'",
            conditional: { json: "", show: "", when: "", eq: "" },
            logic: [],
            attributes: [],
            overlay: {
                style: "",
                page: "",
                left: "",
                top: "",
                width: "",
                height: ""
            },
            type: "panel",
            label: "Panel",
            breadcrumb: "default",
            tabindex: "",
            input: false,
            components: [
                {
                    label: "Date / Time",
                    tableView: false,
                    enableMinDateInput: false,
                    datePicker: {
                        disableWeekends: false,
                        disableWeekdays: false,
                        showWeeks: true,
                        startingDay: 0,
                        initDate: "",
                        minMode: "day",
                        maxMode: "year",
                        yearRows: 4,
                        yearColumns: 5,
                        minDate: null,
                        maxDate: null
                    },
                    enableMaxDateInput: false,
                    key: "dateTime1",
                    type: "datetime",
                    input: true,
                    widget: {
                        type: "calendar",
                        displayInTimezone: "viewer",
                        language: "en",
                        useLocaleSettings: false,
                        allowInput: true,
                        mode: "single",
                        enableTime: true,
                        noCalendar: false,
                        format: "dd/MM/yyyy hh:mm a",
                        hourIncrement: 1,
                        minuteIncrement: 1,
                        time_24hr: false,
                        minDate: null,
                        disableWeekends: false,
                        disableWeekdays: false,
                        maxDate: null
                    },
                    placeholder: "",
                    prefix: "",
                    customClass: "",
                    suffix: "",
                    multiple: false,
                    defaultValue: "",
                    protected: false,
                    unique: false,
                    persistent: true,
                    hidden: false,
                    clearOnHide: true,
                    refreshOn: "",
                    redrawOn: "",
                    modalEdit: false,
                    labelPosition: "top",
                    description: "",
                    errorLabel: "",
                    tooltip: "",
                    hideLabel: false,
                    tabindex: "",
                    disabled: false,
                    autofocus: false,
                    dbIndex: false,
                    customDefaultValue: "",
                    calculateValue: "",
                    calculateServer: false,
                    attributes: [],
                    validateOn: "change",
                    validate: {
                        required: false,
                        custom: "",
                        customPrivate: false,
                        strictDateValidation: false,
                        multiple: false,
                        unique: false
                    },
                    conditional: { show: null, when: null, eq: "" },
                    overlay: { style: "", left: "", top: "", width: "", height: "" },
                    allowCalculateOverride: false,
                    encrypted: false,
                    showCharCount: false,
                    showWordCount: false,
                    properties: [],
                    allowMultipleMasks: false,
                    format: "dd/MM/yyyy hh:mm a",
                    useLocaleSettings: false,
                    allowInput: true,
                    enableDate: true,
                    enableTime: true,
                    defaultDate: "",
                    displayInTimezone: "viewer",
                    timezone: "",
                    datepickerMode: "day",
                    timePicker: {
                        hourStep: 1,
                        minuteStep: 1,
                        showMeridian: true,
                        readonlyInput: false,
                        mousewheel: true,
                        arrowkeys: true
                    },
                    customOptions: [],
                    id: "e20l0yo"
                }
            ],
            placeholder: "",
            prefix: "",
            suffix: "",
            multiple: false,
            defaultValue: null,
            protected: false,
            unique: false,
            persistent: false,
            clearOnHide: false,
            refreshOn: "",
            redrawOn: "",
            labelPosition: "top",
            description: "",
            errorLabel: "",
            hideLabel: false,
            autofocus: false,
            dbIndex: false,
            customDefaultValue: "",
            calculateValue: "",
            calculateServer: false,
            widget: null,
            validateOn: "change",
            validate: {
                required: false,
                custom: "",
                customPrivate: false,
                strictDateValidation: false,
                multiple: false,
                unique: false
            },
            allowCalculateOverride: false,
            encrypted: false,
            showCharCount: false,
            showWordCount: false,
            allowMultipleMasks: false,
            tree: false,
            id: "eng58x9"
        }
    ]
}
TanyaGashtold commented 1 year ago

I am closing the issue as it was created too long ago and there is no new comments here. If the issue is not resolved, please reopen it. Thanks!

Akash-chowrasia commented 9 months ago

Please reopen this issue. I am also getting the same issue. I am not able to pre-populate data on fields even after i tried to update the component data while providing it to the Rorm component to fillup the defaultValue .

Functions I used to update the component:

function fillupDefaultValues(component, prefillData) {
    let result;
    if (component.components) {
      result = component.components.map((comp) =>
        fillupDefaultValues(comp, prefillData),
      );
      return { ...component, components: result };
    } else if (component.columns) {
      result = component.columns.map((comp) =>
        fillupDefaultValues(comp, prefillData),
      );
      return { ...component, columns: result };
    } else {
      if (prefillData[component.key]) {
        result = { ...component, defaultValue: prefillData[component.key] };
      } else {
        result = { ...component };
      }
      return result;
    }
  }
const prefillData = (components, prefillData) => {
    if (!prefillData) {
      return components;
    }
    const data = components.map((component) => {
      return fillupDefaultValues(component, prefillData);
    });
    return data;
  };

Used it as:

<Form form={{
            components: prefillData(components, prefillData),
            }}
      onChange={(changedData) => changeFormData(changedData)}
/>

please suggest some solution to acheve it