- Notifications
You must be signed in to change notification settings - Fork 279
Performance Improvements #66
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
Conversation
This is fantastic! Thanks @bradbumbalough! Could we get this merged in @gitim? Thanks! |
}); | ||
| ||
} else if (data && nextData && !shallowEqual(data, nextData)) { | ||
this.setState({ data: nextData }); |
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.
issue with code change here: this won't update the order when rendering if the order is different
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.
Are you providing the order
prop? It works fine for me, but I might not have factored in if you don't provide it. The goal is that the data gets rerendered without the keys changing. This line was the issue for me: https://github.com/gitim/react-native-sortable-list/pull/66/files/e0f5ce66ba855cf43e85a3f19456c0d5f38e67b3#diff-e7d95e98bb0472979c2ad48071012cb9R94.
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.
yes, I was providing the order
prop. What would happen is that the first if statement is false (since the keys of the data were the same), but the second if statement failed (since the actual shallowEqual
comparison failed).
So this would trigger the state to update just the data
, even though order
should be updated to nextOrder
as well.
Does that make sense?
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 so... is it if you're updating order and data at the same time? whereas right now it's assuming each happen in separate calls?
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.
Correct. Use case for me is that when a user throws away past changes, we want to restore the last savepoint's order and data (hence, updating both at once)
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.
What if we combined the data and order change into one if
branch? would that work?
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 something like this:
else if ((data && nextData && !shallowEqual(data, nextData)) || (order && nextOrder && !shallowEqual(order, nextOrder)) { this.setState({ data: nextData, order: nextOrder });
const {data, order} = this.state; | ||
let {data: nextData, order: nextOrder} = nextProps; | ||
| ||
if (data && nextData && !shallowEqual(data, nextData)) { |
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.
@gitim, the check for shallowEqual
here will always return false when the data is a nested object.
Should https://github.com/gitim/react-native-sortable-list/blob/v0.0.16/src/utils/shallowEqual.js#L17
instead be:
- ignoring object comparisons,
- or doing a deep comparison, via
JSON.stringify(objA[keysA[i]]) !== JSON.stringify(objB[keysA[i]])
?
Thanks!
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.
Do you mean if the data is part of a nested object? (i.e. redux?).
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.
For my case, data is a map of keys to values, where some values are maps themselves. When comparing the maps against each other, shallowEqual
will always return false.
e.g.
nestedObjA = {1: "a", 2: "b", 3: "c"}; nestedObjB = {1: "a", 2: "b", 3: "c"}; // shallowEqual will always return false for data with nested objects myData = {"key" nestedObjA}; nestedObja === nestedObjB; // returns false JSON.stringify(nestedObjA) === JSON.stringify(nestedObjB); // returns true
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.
Interesting.. I think I'm also using nested values (each row represents a model with varied maps and attributes) but I'm not having any issues getting the new orders.
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.
It was my initial intension, because it is not expected that nested objects will be cloned
Thanks for your contribution, I will review that in this week |
let {data: nextData, order: nextOrder} = nextProps; | ||
| ||
if (data && nextData && !shallowEqual(data, nextData)) { | ||
if (data && nextData && Object.keys(data).toString() !== Object.keys(nextData).toString()) { |
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.
In case when values of data changes we need layout recalculation, for example
oldData = {1: 'Some text. Some text. Some text. Some text. Some text.'}
new Data = {1: 'Some text. Some text. Some text. Some text. Some text. Some text. Some text. Some text. Some text. Some text.'}
here a key was not changed, but value changed and a new row will be higher...
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.
And generally about this commit, does is it really bottle neck for performance in your apps? I suppose no, because unlikely data is frequently changing
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.
Is there a way to recalculate the layout without changing the keys? It's not the performance but the screen flash with the rows remounting... any change (typing, etc) causes it to get the new keys.
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 it maybe possible to implement it better, this was not in my mind when I wrote this component, because this feature was not need in our app, I added it after someone in issues asked me.
Currently, the component uses same rows for getting layout and showing to user, it firstly renders rows with right: 9999 and then when new layouts calculated shows them to user. For eliminating flash it should separately render rows for layout calculation and for user presentation
const vx = Math.abs(gestureState.vx) | ||
| ||
return this._active && (this.props.horizontal ? vx > vy : vy > vx); | ||
}, |
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 should allow for child horizontal pan responders (ie swiping).
Do you tested? Or it is just a guess?
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.
Yes I was able to use react-native-swipe-list-view now.
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.
Hey @bradbumbalough , would you mind posting a minimal example? I have been struggling to produce a working sortable and swipeable list for some time now. It would be very appreciated.
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.
Tested with react-native-swipe-list-view too, works, cherry-picked to master. Thanks @bradbumbalough
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.
Hi, I tried it in order to make react-native-sortable-list and react-native-swipe-list-view (with SwiperRow), the row swipes well but is not sortable anymore. Is there a working example somewhere ? Thanks !
- This is a performance improvement when the data object changes but not in a way that requires recalcuating the row layout. A third option in componentWillReceiveProps was also added to update the data when it changes, similar to order.
- Use `manuallyActivateRows={true}` to have the `toggleRowActive` prop exposed to rendered rows. - The out of box timer with pan responder will not activate rows.
- This should allow for child horizontal pan responders (ie swiping).
- Error message was cannot read property 'height' of undefined.
9fdda7c
to 45214ff
Compare Rebased on master. |
What the status of merging this branch? |
We definitely try to solve it as there are already several issues were opened, I'll review your solution again soon. |
Thanks! I'll admit my solution might not be solving it at the root, but it seemed to be stemming from the keys being recreated and then having the screen flashes. What I don't know (and will actually be implementing in our app next week) is how my fix handles content change (i.e. height increasing) but the key being the same. |
I could really use this, I'll test the fork and report any issues. |
Any help needed on this PR? The idea of using the key's to reduce renders seems like a solid approach. |
Could anybody take another stab at this? I am not well versed at animations or "algorithms" 😂 I've created a Snack on Expo since it's all JavaScript: Copy the contents of |
Thanks for this package! this is probably the best sortable implementing I've worked with yet.
Here are some improvements I needed in order to use in my app.
I'm updating the content in the data object frequently but this results in constant rerendering. I traced it down to the
componentWillReceiveProps
method that checks if the data object changes. It then resets the keys causing the rerender. No use ofshouldComponentUpdate
would work on my end since the entire tree gets rebuilt. I corrected this by checking if the data keys change, otherwise it will simply update the state for data.toggleRowActive
method to rendered componentThis was a must for me as I have text input in my row and other Touchable* items... I want to control when the PanResponder should move the row. (Expose toggleActive to children #65)
Currently, the responder will steal horizontal swipes even if
horizontal={false}
(Swipe and Sort #30) which prevents the use of a Swipe row.