-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix mergeCollection bug #66
Conversation
CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅ |
I have read the CLA Document and I hereby sign the CLA |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great and nice catch!
lib/Onyx.js
Outdated
// If `newData` happens to not exist, then set the state back to the previous value because | ||
// if the state is set to an undefined value, then the item reverts to whatever is in | ||
// defaultProps | ||
[subscriber.statePropertyName]: typeof newData !== 'undefined' ? newData : prevState[subscriber.statePropertyName], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use _.isUndefined()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yeah... that's a good call. I guess we do have underscore here.
Updated to use underscore |
lib/Onyx.js
Outdated
// defaultProps | ||
[subscriber.statePropertyName]: !_.isUndefined(newData) | ||
? newData | ||
: prevState[subscriber.statePropertyName], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking more on this... this could maybe be filed under "unrelated" or "not a problem", but I find it interesting (or rather incorrect/inefficient) that if a subscriber is connected to a single key, but it's data hasn't changed at all ... that we are still calling setState()
with previous data?
That's not super great because it means that we will still trigger re-renders on those connected components and for no reason at all. So, probably if we have undefined
then we should do less than set the previous state again and should do... well... nothing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think you make a good point. While it's not a problem currently, it's not a great design either. The reason I put it inside setState
is because that's where it is getting newData
. But now that I look at it closely, newData
should be able to be retrieved before calling setState()
and in the case where it is undefined, we can just skip calling setState()
. How's that sound?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, sounds good to me. 👍
tests/unit/withOnyxTest.js
Outdated
expect(onRender2.mock.calls[0][0].testObject).toStrictEqual({ID: 2}); | ||
expect(onRender2.mock.calls[1][0].testObject).toStrictEqual({ID: 2}); | ||
expect(onRender3.mock.calls[0][0].testObject).toStrictEqual({ID: 3}); | ||
expect(onRender3.mock.calls[1][0].testObject).toStrictEqual({ID: 3}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like my expectation would be... not that this gets called again with same data but not called at all ? Why should it re-render since nothing has changed?
OK, updated. Now with less re-renderings! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM and i like the changes 👍
OK, before merging this, I want to give this a little more testing because I haven't been able to test the most recent changes yet. Once I do that testing, I'll let you know when this has a green-light to merge. |
const onRender2 = jest.fn(); | ||
const onRender3 = jest.fn(); | ||
|
||
// GIVEN there is a collection with three simple items in it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB: I REALLY appreciate the GIVEN/WHEN/THEN
docs here! It makes it so much easier to follow what's going on and what we're testing for. :bigfan:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I love that format for testing comments. @nkuoch was the one that originally showed it to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me
OK, I think I found another bug in my testing, but I want to write a test case to confirm that theory. Will continue this on Monday. |
a8ab34e
All right, I am taking off the hold here. When I tested this on Friday I found another bug with how the new collection data was being merged into the state. I have added a test case to cover that, fixed the bug (using spread operators instead of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
subscriber.withOnyxInstance.setState(prevState => ({ | ||
[subscriber.statePropertyName]: _.isObject(dataFromCollection) | ||
? { | ||
...prevState[subscriber.statePropertyName], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems potentially dangerous as it assumes we will always have at least an empty object for prevState[subscriber.statePropertyName]
if we are dealing with object shaped data. If we try to spread something like undefined
or null
then we get 💥.
Maybe some combination of lodash.merge()
could still be helpful here to help guard against that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe there's no problem with that actually. I could just have made that up in my head.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think spreading undefined
in objects works OK but arrays doesn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the small difference between this and using lodashMerge()
is that the spread is not going to recursively merge the data. So depending on how complicated that data is we could get some weird results. But we can address that if it becomes a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, thanks Marc. I think it'll be OK for now. Seeing as how everyone has approved this, I'm going to go ahead and merge it.
Details
This fixes a bug found in mergeCollection
Related Issues
https://github.com/Expensify/Expensify/issues/161384
Automated Tests
There is a test added which properly tests the behavior
Linked PRs
Expensify/App#2543