-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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 Inverted VirtualizedList's baseline measurement for items' offset (1st of 4 problems that cause #1254) #2412
Conversation
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 5482ad5:
|
@necolas Let me know what you think, I'm happy to answer any questions. |
Never seen a PR so well detailed. Nice job on 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.
Hi, thanks for taking the time to explain your PRs in detail. Several of them appear to not be web-specific and so I wonder if you've looked into how much of this code should be upstream in react-native? The VirtualizedList code is almost entirely copy-pasted from react-native every few months, and web-specific code is likely to get lost each time that happens. If you can identify only the code that needs to exist in the react-native-web fork, that would be a big help. Thanks
packages/react-native-web/src/modules/useElementLayout/index.js
Outdated
Show resolved
Hide resolved
packages/react-native-web/src/vendor/react-native/VirtualizedList/index.js
Outdated
Show resolved
Hide resolved
Thank you @necolas, I didn't know about the connection between react-native-web and react-native. I thought components in react-native, like VirtualizedList, were purely written in native code. I will review my code with these insights and make PRs to the according repos. |
packages/react-native-web/src/vendor/react-native/VirtualizedList/index.js
Outdated
Show resolved
Hide resolved
Hi @necolas , Just gut checking, I am planning to find a solution to make Web onLayout 'y' behave the same as RN's onLayout 'y' in inverted VirtualizedList. Do you think this would be a good path to follow? In case you agree, do you know how I can test onLayout in multiple scenarios so I can't break something? |
Are these changes being submitted to react-native? |
@LucioChavezFuentes Thanks! Please close any PRs here that you no longer believe necessary. |
I think this is probably a good idea because we're also trying to make these lists npm packages that react-native-web can import, rather than having to maintain forked versions. And there may be other scenarios in which the difference in |
Great details. |
Update:
This proposal is deprecated, New proposal here
I found 4 problems that cause the Inverted VirtualizedList to go wild (#1254). This PR will explain and fix the 1st of 4 problems found.
If we fix the following 4 problems, issue #1254 will be fixed and FlatList’s scroll will be more stable:
Update:
PR 2, 3, and 4 will fix 'Flatlist with expensive items breaks scroll' issue. PR 1 is enough to fix the Inverted Flatlist issue if your Flatlist's items are cheap to mount.
$lead_spacer expands scroll artificially when Inverted VirtualizedList is mounting new items —> Problem Explanation and Solution in this PR.
VirtualizedList skip items for offset measuring when the user scrolls very fast while new items are mounted and measured (this happens also for normal lists) —> Problem Explanation and Solution in this PR.
VirtualizedList gets offsets equal to zero for items that are not the list's first item (this happens also for normal lists) —> Problem Explanation and Solution in this PR.
Inverted FlatList with all problems fixed
2022-10-23.17-49-22.mp4
1st Problem
Inverted VirtualizedList has incorrect baseline measurement for its items’ offset. (1st of 4 problems that cause #1254)
FlatList uses a virtual area (VirtualizedList) where it only renders the elements we need to see. In a FlatList with items with different heights (like report chat list), VirtualizedList mounts and measures the ‘y’ offset position of each item and saves it on an object called
_frames
.Once the offset of an item is saved, VirtualizedList (on each scroll) can safely unmount the measured item if it is out of the virtual area. And mount it back if the offset position is in the current virtual area. So an accurate and complete
_frames
is fundamental for a smooth and fast scroll on FlatList.Inverted FlatList has inaccurate offset values in the object
_frames
because it measures items’ offsets (y
position) as baseline measurement at the top (y = 0
), but the scroll view’s offset baseline measurement is at the bottom.Why?
Inside OnLayout’s react-native-web implementation,
y
offsets measures are done withgetBoundingClientRect()
. This API measures the DOM elements offsets as we see it, withy = 0
at the top of our viewport (as a starting point of measure).A normal FlatList looks and measures like this:
The inverted FlatList uses
{transformY: -1}
that inverts not only our items but also our initial scroll position of the scroll view.y = 0
is now at the bottom of our scroll view:But Inverted FaltList still measures item's offsets using
getBoundingClientRect()
(withonLayout
) withy = 0
at the top:As you can see the first items have the greatest offsets and our last items have offsets closer to zero. But on each scroll, FlatList's scroll view expects the opposite. The problem gets worse with longer lists because our first item will have the greatest offset, but it will be based only within the virtualized area, not on our entire list.
Solution
Use items’
offsetTop
instead of onLayout’sy
.offsetTop
gets the correct offset for each item in normal or inverted lists because it ignores parent's transformations (liketransform: [{scaleY: -1}]
).react-native-web/src/vendor/react-native/VirtualizedList/index.js