final-form-arrays icon indicating copy to clipboard operation
final-form-arrays copied to clipboard

Fix and optimize all mutators

Open huan086 opened this issue 5 years ago • 5 comments
trafficstars

This pull request depends on https://github.com/final-form/final-form/pull/393

Fixes #58 Closes #43 Closes #45

I have edited the unit tests to trigger bugs, or to remove testing of implementation details.

Potentially breaking change

Change remove and removeBatch behavior to set array value to undefined when all items have been removed

I think this change would make pristine calculation more correct

huan086 avatar Nov 12 '20 14:11 huan086

Codecov Report

Merging #61 (59e9137) into master (4a65647) will not change coverage. The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff            @@
##            master       #61   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           15        13    -2     
  Lines          165       175   +10     
  Branches        36        37    +1     
=========================================
+ Hits           165       175   +10     
Impacted Files Coverage Δ
src/copyField.js 100.00% <100.00%> (ø)
src/insert.js 100.00% <100.00%> (ø)
src/move.js 100.00% <100.00%> (ø)
src/pop.js 100.00% <100.00%> (ø)
src/remove.js 100.00% <100.00%> (ø)
src/removeBatch.js 100.00% <100.00%> (ø)
src/swap.js 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 4a65647...71bebc7. Read the comment docs.

codecov[bot] avatar Nov 12 '20 15:11 codecov[bot]

Don't merge yet. I realize I can simplify the logic and get rid of the sorting on fieldIndex

huan086 avatar Nov 12 '20 16:11 huan086

I hope that it'll be merged soon.

souljja avatar Nov 27 '20 08:11 souljja

@erikras Could you tell if you gonna review/merge it soon? Because right now shift and insert methods are broken and I really need them in my application.

souljja avatar Dec 16 '20 10:12 souljja

@erikras pinging you on this. There seem to be some pretty fundamental issues that need to be addressed in this lib.

Thanks for all the work!

jspizziri avatar Dec 15 '21 20:12 jspizziri

This change broke our app because suddenly field array value was undefined instead of empty array when last item was removed. I don't understand the logic.

heikki avatar Mar 09 '23 11:03 heikki

The main bug fix in this PR fixed all the mutators except push that were broken and basically unusable.

The case @huan086 made was quite valid that the arrays behave differently when you clear them all other fields default to undefined in the empty state and this leads to pristine being wrong when your do not define initial values and then add and remove all elements from the array. Note that the new release was a minor change and not just a patch which is guaranteed to be compatible.

To get the behaviour as it was previously you can have a look at an identity function for the parse callback.

gertdreyer avatar Mar 09 '23 19:03 gertdreyer

this leads to pristine being wrong when your do not define initial values and then add and remove all elements from the array

But if you have defined an initial value of [], shouldn't it go back to it if you add and remove all items? Now it always goes to undefined.

peruukki avatar Apr 19 '23 11:04 peruukki

The strings in FF have been working with this approach.

If you define an empty string as an initial value and then add something to the field and remove it you end up with an undefined value after the change. The behaviour you want is not implemented for strings either (why should arrays be treated differently?) The change is quite simple and is documented in how to accomplish the alternative you need at https://final-form.org/docs/react-final-form/types/FieldProps#parse

Compare the sequence below in the codesandbox in the firstName field and lastName field. firstName is the default behaviour, while lastName gets the behaviour you need.

https://codesandbox.io/s/zealous-julien-4qlo0l?file=/index.js

The initial value for both: "" Enter data in the fields, and clear the data from the fields. For firstName it is undefined. For lastName it is "".

gertdreyer avatar Apr 20 '23 17:04 gertdreyer

Thanks for the info! I agree arrays should work the same way as strings, though I guess that just means I would also like strings to work differently. 😅 (Though not expecting that.)

Thanks for pointing out parse. 👍 It bothers me a bit that it can be easy to forget to add it, especially since an identity function looks like it does nothing, so it has surprising side effects. I know it's documented but it's definitely a gotcha for people who are not that familiar with the library.

Maybe there could be an option in e.g. FormProps to specify what kinds of default values to use, similarly to keepDirtyOnReinitialize? I understand you will want to keep the interface as small as possible, but just a suggestion or food for thought. 🙂 Ideally, that choice would propagate to the type definition of the field's value too, but not sure if that's even possible.

peruukki avatar Apr 21 '23 06:04 peruukki

The behaviour you want is not implemented for strings either (why should arrays be treated differently?)

I agree arrays should work the same way as strings [...]

Actually, it came to my mind that arrays are different — we even have this separate package for dealing with them. I believe in the most common use case, the form value contains values for a collection of inputs, not for a single input. So it's an added layer that doesn't correspond to a native input, and therefore I think a different behavior would be justifiable.

peruukki avatar Apr 25 '23 06:04 peruukki

Hi @peruukki, as the author of this pull request, I don't like the current behaviour actually. The decision to make values undefined dates back to redux-forms, and I think it was designed this way so that pristine state gets computed correctly, where initial state is set to undefined and an object gets added and removed, thus setting it back to undefined. undefined === undefined, thus form state is pristine, while in the alternative [] === [] is false and would cause a false positive that the form has changes.

Ideally, the initial value should be consulted, so that pristine calculation is even more correct. E.g. Initial [] after adding and remove should go back to the same reference in the initial value. Or even a step further, if initial state is [1234] and items are added and removed, and ends up with [1234] again, the final [1234] should be the same reference as the initial value.

I am not sure how to implement the ideal behaviour while keeping the performance of the code. The current react-final-forms already performs poorly compared to redux-forms where I keyed array items with an id.

Please contribute to make this library better when you have the time, thanks!

huan086 avatar Apr 25 '23 07:04 huan086