final-form / final-form-calculate

Decorator for calculating field values based on other field values in 🏁 Final Form
MIT License
113 stars 26 forks source link

Update gets triggered on render with initialValues #26

Open AdamGerthel opened 5 years ago

AdamGerthel commented 5 years ago

Are you submitting a bug report or a feature request?

Not sure if it's a bug or a if it works as intended

What is the current behavior?

Updates are triggered once when then the form is rendered, i.e. even though no changes have been made to the form values.

See https://codesandbox.io/s/0ml4k745x0

What is the expected behavior?

I would expect updates to be triggered only on updates, not by the initial values.

What's your environment?

    "react": "^16.6.3",
    "react-dom": "^16.6.3",
    "react-final-form": "^4.0.2",
    "final-form": "^4.11.0",
    "final-form-calculate": "^1.3.0"
Babbz75 commented 5 years ago

Did anyone find a work around for this?

Soundvessel commented 5 years ago

Perhaps you can use the field's pristine prop to watch for a change on that specific field? Pristine will respect initialValues. https://github.com/final-form/final-form#pristine-boolean

Edit: Although I don't see the API able to get meta of the field?

nik-lampe commented 5 years ago

Interesting. This does not happen, when you specify the field as a regex. And, it gets triggered, but it seems that the changes won't be applied to the values.

Came across this, because I'm looking for a way to do a initial calculation with the initial values

Soundvessel commented 5 years ago

Just a thought but is there a chance the form is rendered before the initial values are available and adding them to an existing form could be the culprit? This could be confirmed either with the React profiler tool or logic to prevent render until you have the data for initialValues ready?

Dragomir-Ivanov commented 5 years ago

I think that this is valid behavior. I am using exactly this functionality, in order to calculate additional fields, based on initial ones.

AdamGerthel commented 5 years ago

@Dragomir-Ivanov Right, but just because use exploit that fact that it functions like this doesn't mean that it's expected behaviour.

I personally would expect an "update" hook to run on updates only, not creation/initialisation. I mean, that could easily be handled outside of Final Form entirely.

I do think that this is a an issue that would potentially cause a new major release since it might break a lot of existing uses of this plugin.

AdamGerthel commented 5 years ago

Just a thought but is there a chance the form is rendered before the initial values are available and adding them to an existing form could be the culprit? This could be confirmed either with the React profiler tool or logic to prevent render until you have the data for initialValues ready?

@Soundvessel If you look at my sandbox sample in the original post, you'll see that the value is there from the start.

Dragomir-Ivanov commented 5 years ago

@AdamGerthel You may be right. For the time being I guess you can fork your own Decorator, final-form-calculate is rather small, until this issue is settled.

cordial commented 3 years ago

Is there any way round this? I load my initial values from a DB and this sets off an update which can make the values different from the DB values.

leonardopolly commented 3 years ago

@cordial, I got to work around this by wrapping createDecorator function in my own really small decorator that returns the original decorator called with form as its argument. In the updates property you can now access form and check if it is pristine. If it is, simply return an empty object :)

Instead of writing

const myDecorator = createDecorator({
  field: 'fieldA',
  updates: {
    b: (fieldAValue, allValues) => { 
      //your calculation 
    }
  }
});

you can write

const myDecorator = form => createDecorator({
  field: 'fieldA',
  updates: form.getState().pristine? {} : {
    b: (fieldAValue, allValues) => { 
      //your calculation 
    }
  }
})(form)
dozen1488 commented 3 years ago

@cordial, I got to work around this by wrapping createDecorator function in my own really small decorator that returns the original decorator called with form as its argument. In the updates property you can now access form and check if it is pristine. If it is, simply return an empty object :)

Instead of writing

const myDecorator = createDecorator({
  field: 'fieldA',
  updates: {
    b: (fieldAValue, allValues) => { 
      //your calculation 
    }
  }
});

you can write

const myDecorator = form => createDecorator({
  field: 'fieldA',
  updates: form.getState().pristine? {} : {
    b: (fieldAValue, allValues) => { 
      //your calculation 
    }
  }
})(form)

How can you get access to the form state, when you need to set decorator to render form, and only inside form render callback you can get form state.

leonardopolly commented 3 years ago

@dozen1488

How can you get access to the form state, when you need to set decorator to render form, and only inside form render callback you can get form state.

According to the official docs, a decorator is a function that receives the form api as a parameter, subscribes to it, making changes as the state changes, and returns an Unsubscribe function. https://final-form.org/docs/final-form/types/Decorator

So when you create a decorator using the createDecorator function, that function call is returning another function in the shape of formApi => { // whatever change you told the decorator to make }

MrBlackRain commented 8 months ago

Sorry for necroposting. I recently stumbled across the same problem. @leonardopolly suggestion didn't work for me in case when updates is a function, not an object. So I just put an if statement inside a function, like this

const myDecorator = form => createDecorator({
  field: 'fieldA',
  updates: (fieldAValue, fieldName, allValues, prevAllValues) => { 
      if (form.getState().pristine) return {}
      //your calculation 
    }
  }
})(form)

Maybe, it will be useful for somebody.