4

I have a form that uses final-form-arrays. The form works and validation works, however, when i make a state change inside the component, it resets all my values.

I was able to replicate the issue with the same example that react-final-form-arrays provides:

https://codesandbox.io/embed/react-final-form-field-arrays-om6p6

I added a button to toggle a state change. Essentially, just try filling values in the form, and then change the state. The form will reset, and i cant figure out why that is the case. If i remove initialValues, this does not happen.

Does anyone know why that may be?

xunux
  • 1,531
  • 5
  • 20
  • 33
  • 1
    We have been facing this issue for a long time If anyone can help solve this it will be great !! – Raj Saraogi Jun 05 '19 at 06:26
  • Raj, user2119597 below pointed out a possible solution, however, it seems like it doesnt solve my problem 100% since i have other state-dependent fields in there and that method seems to make the form ignore state changes. – xunux Jun 05 '19 at 15:59
  • Yes the solution also seems a bit tricky to me. Still searching for full proof solution. – Raj Saraogi Jun 12 '19 at 03:59

2 Answers2

5

2 problems.

  1. You're passing an inline renderProp to the Form component, this means it's creating a new function every time your component renders which causes your form to reset. The solution is to move the renderProp into a function above and pass that in, ideally memoized with useCallback.
  2. Once you fix the above the form will still reset. This is because you're passing an inline array to initialValues. This again will create a new array every time your component renders which resets your form. Move it into a variable and pass it in.

This is a pretty common beginner mistake, you should read up on how react does reference equality checks to figure out which components to re-render.

Fixed version: https://codesandbox.io/embed/react-final-form-field-arrays-c6hgu

Raza Jamil
  • 264
  • 1
  • 3
  • 12
  • thanks so much! it's definitely working in the example. I have another question, how would this change if instead of using the render prop i used a function inside of
    as a child of the form? In the example i used the render prop simply because that's the example the docs give, but in my code i tend to use a child function.
    – xunux Jun 05 '19 at 14:56
  • oh and one more question, in other forms in which i dont use arrays, i pass inline values to initialValues but it does not cause a form reset. How come in this case it does? If i remove the array value and only provide inline values it doesnt reset the form – xunux Jun 05 '19 at 14:58
  • 1
    This solution seems to be causing some other undesired effects. I have some elements inside the form that respond to state changes, and having my whole form inside `useCallback()` since it is memoized, it does not respond to state changes anymore. If I provide the state as the dependency to setCallback so it does respond to state changes, then we are back to square 1 because the form resets. This is just to change the text in the submit button while submitting – xunux Jun 05 '19 at 15:58
  • 1
    Q. how would this change if instead of using the render prop i used a function inside of
    as a child of the form? A. Same thing, a renderProp is actually a component which is actually just a javascript function. Q. oh and one more question, in other forms in which i dont use arrays, i pass inline values to initialValues but it does not cause a form reset. How come in this case it does? A. Objects/arrays/functions are compared by reference where as primitive values (strings/numbers etc) are compared by value.
    – Raza Jamil Jun 14 '19 at 18:16
  • 1
    Q. This solution seems to be causing some other undesired effects... A. You can memoize just the renderProp sent to the form. Looking at this again, it's probably overkill to memoize the whole form component. – Raza Jamil Jun 14 '19 at 18:18
0

Just memorize your initialValues in an arrow function then pass it to the form component:

const initialValues = useCallback(() => {
    return {data: [{}]};
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Pass the function as your Form component's initialValues prop.

Adera
  • 27
  • 1
  • 7