final-form / react-final-form

🏁 High performance subscription-based form state management for React
https://final-form.org/react
MIT License
7.39k stars 481 forks source link

Initial Value for a field is set again even if the value for that field has been changed to something else. #536

Closed vidur149 closed 5 years ago

vidur149 commented 5 years ago

bug report

What is the current behavior?

I am using wizrard form. The form in total has three pages. On the basis of field values of the first page, second page field values are generated from an API request. We are sending some default values from our backend and I am setting the initialValue for the field. Everything works fine for the first time but as soon as I navigate to third page and come back to the second page, whatever change I have made to the field values are lost and the fields take up the initial values.

What is the expected behavior?

The field should retain the changed values.

Second page fields

renderCheckboxes = field => (
    <Col className="f-form-row" xs={12} key={field.label}>
      <Row>
        <Col xs={12}><h3>{field.label}</h3></Col>
        {field.options.map(option => (
          <Col xs={12} key={option.value}>
            <Field
              component={CheckboxField}
              name={option.value}
              label={option.label}
              type="checkbox"
            />
          </Col>
        ))}
      </Row>
    </Col>
  )

  renderTextField = field => (
    <Col className="f-form-row" xs={4} key={field.name}>
      <Field
        component={TextField}
        label={field.label}
        name={field.name}
        initialValue={field.default}
        type={field.type}
        required={field.required}
        validate={value => (field.required ? validationhelper.required(value) : undefined)}
      />
    </Col>
  );

  renderSelectField = field => (
    <Col className="f-form-row" xs={4} key={field.name}>
      <Field
        component={SelectField}
        isSearchable
        name={field.name}
        options={field.options}
        initialValue={field.default && {
          label: field.default,
          value: field.default
        }}
        label={field.label}
        required={field.required}
        validate={value => (field.required ? validationhelper.required(value) : undefined)}
      />
    </Col>
  )

  render() {
    const {
      dbConn, setDbConn, platformDetails
    } = this.props;

    if (!platformDetails) {
      return <Loading />;
    }

    const { metadata: { modes, parameters } } = platformDetails;

    return (
      <>
        <Row className="f-form-row">
          <Col className="d-flex align-items-center">
            <h2 className="mdh-light m-0 mr-3">Direct DB Connection</h2>
            <ToggleSwitch
              disabled={modes.length === 0}
              onChange={() => setDbConn(!dbConn)}
              checked={dbConn}
            />
          </Col>
        </Row>
        <Row>
          {parameters.map((field) => {
            if (field.type === 'multiselect' && this.shouldRenderField(field.modes)) {
              return this.renderCheckboxes(field);
            }

            if (field.type === 'select' && this.shouldRenderField(field.modes)) {
              return this.renderSelectField(field);
            }

            return this.shouldRenderField(field.modes) && this.renderTextField(field);
          })}
        </Row>
        <EnableBPM />
      </>
    );
  }
}

What's your environment?

🏁 React Final Form version 6.1.0 🏁 Final Form version 4.13.1 OS/browser affected Mojave/Chrome React 16.8.6

ndrsllwngr commented 5 years ago

I second your opinion on the expected behaviour, <Field .. initialValue={..} should allow for a non-resetting initialValue experience as does the initialValues on the form level.

Workaround

Try using initialValues on the entire form instead of on the field level. If that is not possible, you may take a look at this intermediate workaround by @Andarist https://github.com/final-form/react-final-form/issues/533#issuecomment-502343047 using hooks.

vidur149 commented 5 years ago

@ndrsllwngr I can use the hooks solution because I am using classes in my react component. I will have to rewrite the entire thing and learn hooks. :/

Andarist commented 5 years ago

You should be able to implement similar logic based on FormSpy using double render technique - render null at first just to get access to the form object from FormSpy and setState to render the desired component.

vidur149 commented 5 years ago

@Andarist can you please elaborate on this?

vidur149 commented 5 years ago

@Andarist I tried many hacks with FormSpy, not able to make even a single one work. Help would be appreciated, as we are using react-final-forms in couple of places and I love using the library.

Somehow, the initialValue seems to cause a trouble in other cases as well. Like in other place if I set field level initialValue(x) for a field A and change its value from x to y, and then I make some changes to underlying fields, field A takes back the x value. This is very weird behaviour for me.

vidur149 commented 5 years ago

@erikras can you help?

erikras commented 5 years ago

Fix published in final-form@4.18.6.