final-form-arrays
final-form-arrays copied to clipboard
Fixes unshift and insert row not update field's state correctly
as reported in https://github.com/final-form/final-form-arrays/issues/44 and https://github.com/final-form/react-final-form-arrays/issues/138.
This applied both unshift
and insert
because unshift
use insert
as this
https://github.com/final-form/final-form-arrays/blob/fe3c260939cbdd446a48304dd2df0be2a1c41237/src/unshift.js#L5-L9
I've investigated and have assumed the issue is in the following.
https://github.com/final-form/final-form-arrays/blob/fe3c260939cbdd446a48304dd2df0be2a1c41237/src/insert.js#L18-L35
the logic is trying to shift the field's state to one for all fields that are greater than or equal to inserted index. For the field's state at inserted index, it will leave as empty because there's a return statement here.
https://github.com/final-form/final-form-arrays/blob/fe3c260939cbdd446a48304dd2df0be2a1c41237/src/insert.js#L26-L37
For example, if there're 4 field array items and we used insert(2, value)
, the field's state will shifted like this
newState[0] = currentState[0]
newState[1] = currentState[1]
// newState[2] will be empty
newState[3] = currentState[2]
newState[4] = currentState[3]
this behavior, as I understand, it's intended and should be correct. because when the state is empty, it'll get the default state from here
https://github.com/final-form/final-form/blob/d20c44be8766b93424e7754fe2629820fd93d21a/src/FinalForm.js#L850-L866
which should set the default for the missing state. but the problem is related to React component's key
prop
Since the component is rendered by each item in fields
, as a convention and it should be, provided with name
from fields
. so the field component is reused with the same component as the previous, one before insert(2, value)
is called and after because name
are the same. So method registerField
which should set the field's state as default, doesn't trigger. this makes the field's state incorrect and leads to rendering problems.
I've tried to make the key unique and found that it render correctly. but as we know, this is not a good solution.
https://github.com/final-form/final-form-arrays/assets/13183413/16fd8398-c523-41de-a7e2-d3a669ef94b9
Here are the possible solutions I can think of
-
name
should not be used as a key and the final form array provides another variable for this purpose instead.
Since the problem is about the rendering of components, we might need to provide a key
which is different before and after insert
. But I can't think of how we should generate a key that is new every time we insert a new index but also it should be generated based on an index. This is quite a conflict. so I have no idea about this. anyways, this should be done in https://github.com/final-form/final-form-arrays and shouldn't relate here.
-
Just remove the shifting state and keep only copy value as suggested but this https://github.com/final-form/react-final-form-arrays/issues/138#issuecomment-681958884 which I still do not understand why it works as the field's state at inserted index just missing. Anyways, It might be some edge cases that lead to errors.
-
Add default state for the inserted field. IMO, this might be the safest solution, which try to add default as intended. Fortunately, in the mutator, there's a helper function
resetFieldState
for resetting the field's state.
My PR is to use solution 3, as far as I have checked, it has no problem. I checked with validation for the array, shows an error at submit (this is required the use of the field's state).
(the validation is, the name field must have string "t") https://github.com/final-form/final-form-arrays/assets/13183413/5c055b9e-9046-49f9-8974-d98a99df5954
For anyone, who can't wait for the fix, and need a quick solution now (I can feel that), I provided the gist you can copy and use the functions as the mutator.
https://gist.github.com/Elecweb/7f3478b01126e40ce98492de32d3f995
I hope this can help anyone get some insight into the issue and solutions 🙏
I had similar problems.
After many experiments, I came to the conclusion that I should abandon using an array in favor of normalized data - an array of IDs and a data object. This also allowed me to solve the issues with the unshift and move mutators. https://codesandbox.io/s/react-final-form-rows-field-array-alternative-react-dnd-as-drag-drop-6vrpy6 https://github.com/makhnatkin/react-final-form-rows/blob/main/src/useRows.ts
it also helped me solve performance problems. https://github.com/final-form/react-final-form/issues/336#issuecomment-1700237076
@erikras Hello, could you have a look at this?