Search code examples
react-final-formfinal-form

How to set React Final Form values on request and validate after that?


I have kind of a convoluted use case here. I have an API that responds with a JSON that maps to React components, including form fields (their values, validations, etc). Every component in the page receives props from the backend, so if I make a request I have to update those props, that's why I save the props to state and call setState after every request that should re-render the components.

So I have a RFF with two fields. The form initial value comes from the backend as well as the fields' initial validations (the json maps to validation functions).

I am using this Auto Save with debounce example to send a request to the backend every time the user completes the form and it's valid. Now comes the tricky part for me.

The form must receive the new values from the backend (I do this by calling a function that builds the form values from the page state inside <Form initialValues={fn} />, that causes the form to re-render with the new values in sync with the state) but it also receives the new validations (they can change depending on the values, for example, if field 2 should be at least 20% of field 1), but the form only shows validations on blur or when an input changes.

Also, the fields are text+dropdowns, and when I select an option another request is sent with the new dropdown value (this is desired behavior) but then the response comes in and it updates the fields' values and then ANOTHER request is sent because the inputs changed (this is NOT a desired behavior, and it has to do with the Auto Save example, the diff part).

So, to try and explain it better:

  1. API call is made, responds with something like:
field1: {
  value: '500',
  dropdown_value: 'A',
  validation_value: '100',
  validation_msg: 'value must be 100 or more', // this depends on validation_value
},
field2: {
  value: '1000',
  dropdown_value: 'B',
  validation_value: '300',
  validation_msg: 'value must be 300 or more', // this depends on validation_value
},
// other components props...
  1. Page is rendered with those values as initial form values
  2. User types 600 in the first field
  3. <AutoRequest /> fires a debounced request with response:
field1: {
  value: '600',
  dropdown_value: 'A',
  validation_value: '100',
  validation_msg: 'value must be 100 or more',
},
field2: {
  value: '1000',
  dropdown_value: 'B',
  validation_value: '9999', // changed
  validation_msg: 'value must be 9999 or more', // changed, now the field is invalid
},
// other components props...
  1. The form should be invalid but validation happens on change or blur so it doesn't show any errors nor disables the submit button (ERROR 1)
  2. Now let's assume the form is valid. The user selects a new option from the first dropdown, this changes form values so <AutoRequest /> fires a new debounced request with response:
field1: {
  value: '7777', // changed, value transformed in the backend
  dropdown_value: 'A2', // changed
  validation_value: '7777', // changed
  validation_msg: 'value must be 7777 or more', // changed but valid
},
field2: {
  value: '8888', // changed
  dropdown_value: 'B2', // changed
  validation_value: '8888', // changed
  validation_msg: 'value must be 8888 or more', // changed but valid
},
// other components props...
  1. Because the value from at least one input changed after the request, another request is fired up but the API returns the same, so this is undesired (ERROR 2)

I know it's a long post, I'm sorry, but this has been troubling me for a while and I've solved issues in isolation, but when all of these requirements come together I can't seem to find a solution that fits every need of my use case.


Solution

  • I ended up with a solution that I don't like too much but it gets the job done for my use case: https://codesandbox.io/s/ejemplo-react-final-form-nrpgbp

    It simulates an auto request that changes the inputs, but it's also fired every time a form value is changed, so in this case is only for testing the change in a dropdown (if you change the inputs, it will infinitely send new requests. If you want to test that, you will have to modify the request response to match your inputs' current values). Also, the validations change on request and they are fired up as the form re-renders instead of on change or blur.