Skip to content
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

[0.72] Cherry pick - VirtualizedList fixes #37258

Conversation

bang9
Copy link
Contributor

@bang9 bang9 commented May 4, 2023

Summary:

Cherry picks the following onto 0.72-stable:

Changelog: [Internal]

Test Plan:

--

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label May 4, 2023
@github-actions
Copy link

github-actions bot commented May 4, 2023

Warnings
⚠️

packages/rn-tester/js/examples/SectionList/SectionList-scrollable.js#L285 - packages/rn-tester/js/examples/SectionList/SectionList-scrollable.js line 285 – Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “SectionList_scrollable” and pass data as props. If you want to allow component creation in props, set allowAsProps option to true. (react/no-unstable-nested-components)

⚠️

packages/rn-tester/js/examples/SectionList/SectionList-scrollable.js#L289 - packages/rn-tester/js/examples/SectionList/SectionList-scrollable.js line 289 – Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “SectionList_scrollable” and pass data as props. If you want to allow component creation in props, set allowAsProps option to true. (react/no-unstable-nested-components)

Generated by 🚫 dangerJS against 96d7c92

Summary:
Hey,

`adjustCellsAroundViewport` function was checking if `props.initialScrollIndex` is truthy and -1 was returning true. This caused bugs with rendering for tvOS: react-native-tvos/react-native-tvos#485 There are warnings in the code about `initalScrollIndex` being smaller than 0 but this if statement would still allow that.

## Changelog:

[General] [Fixed] - Make sure initialScrollToIndex is bigger than 0 when adjusting cells

Pull Request resolved: facebook#36844

Test Plan: Pass -1 as initialScrollToIndex. Check that this code is executed.

Reviewed By: cipolleschi

Differential Revision: D44856266

Pulled By: NickGerleman

fbshipit-source-id: 781a1c0efeae93f00766eede4a42559dcd066d7d
@analysis-bot
Copy link

analysis-bot commented May 4, 2023

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 8,546,019 -171,470
android hermes armeabi-v7a 7,860,546 -167,721
android hermes x86 9,025,040 -179,811
android hermes x86_64 8,880,322 -177,921
android jsc arm64-v8a 9,146,144 -135,860
android jsc armeabi-v7a 8,336,737 -133,506
android jsc x86 9,199,951 -140,759
android jsc x86_64 9,457,952 -139,806

Base commit: 74987b6
Branch: main

Summary:
`maintainVisibleContentPosition` is broken when using virtualization and the new content pushes visible content outside its "window". This can be reproduced in the example from this diff. When using a large page size it will always push visible content outside of the list "window" which will cause currently visible views to be unmounted so the implementation of `maintainVisibleContentPosition` can't adjust the content inset since the visible views no longer exist.

The first illustration shows the working case, when the new content doesn't push visible content outside the window. The red box represents the window, all views outside the box are not mounted, which means the native implementation of `maintainVisibleContentPosition`  has no way to know it exists. In that case the first visible view is facebook#2, after new content is added facebook#2 is still inside the window so there's not problem adjusting content offset to maintain position. As you can see Step 1 and 3 result in the same position for all initial views.

The second illustation shows the broken case, when new content is added and pushes the first visible view outside the window. As you can see in step 2 the view facebook#2 is no longer rendered so there's no way to maintain its position.

#### Illustration 1

![image](https://user-images.githubusercontent.com/2677334/163263472-eaf7342a-9b94-4c49-9a34-17bf8ef4ffb9.png)

#### Illustration 2

![image](https://user-images.githubusercontent.com/2677334/163263528-a8172341-137e-417e-a0c7-929d1e4e6791.png)

To fix `maintainVisibleContentPosition` when using `VirtualizedList` we need to make sure the visible items stay rendered when new items are added at the start of the list.

In order to do that we need to do the following:

- Detect new items that will cause content to be adjusted
- Add cells to render mask so that previously visible cells stay rendered
- Ignore certain updates while scroll metrics are invalid

### Detect new items that will cause content to be adjusted

The goal here is to know that scroll position will be updated natively by the `maintainVisibleContentPosition` implementation. The problem is that the native code uses layout heuristics which are not easily available to JS to do so. In order to approximate the native heuristic we can assume that if new items are added at the start of the list, it will cause `maintainVisibleContentPosition` to be triggered. This simplifies JS logic a lot as we don't have to track visible items. In the worst case if for some reason our JS heuristic is wrong, it will cause extra cells to be rendered until the next scroll event, or content position will not be maintained (what happens all the time currently). I think this is a good compromise between complexity and accuracy.

We need to find how many items have been added before the first one. To do that we save the key of the first item in state `firstItemKey`. When data changes we can find the index of `firstItemKey` in the new data and that will be the amount we need to adjust the window state by.

Note that this means that keys need to be stable, and using index won't work.

### Add cells to render mask so that previously visible cells stay rendered

Once we have the adjusted number we can save this in a new state value `maintainVisibleContentPositionAdjustment` and add the adjusted cells to the render mask.

This state is then cleared when we receive updated scroll metrics, once the native implementation is done adding the new items and adjusting the content offset.

This value is also only set when `maintainVisibleContentPosition` is set so this makes sure this maintains the currently behavior when that prop is not set.

### Ignore certain updates while scroll metrics are invalid

While the `maintainVisibleContentPositionAdjustment` state is set we know that the current scroll metrics are invalid since they will be updated in the native `ScrollView` implementation. In that case we want to prevent certain code from running.

One example is `onStartReached` that will be called incorrectly while we are waiting for updated scroll metrics.

## Changelog

[General] [Fixed] - Fix VirtualizedList with maintainVisibleContentPosition

Pull Request resolved: facebook#35993

Test Plan:
Added bidirectional paging to RN tester FlatList example. Note that for this to work RN tester need to be run using old architecture on iOS, to use new architecture it requires facebook#35319

Using debug mode we can see that virtualization is still working properly, and content position is being maintained.

https://user-images.githubusercontent.com/2677334/163294404-e2eeae5b-e079-4dba-8664-ad280c171ae6.mov

Reviewed By: yungsters

Differential Revision: D45294060

Pulled By: NickGerleman

fbshipit-source-id: 8e5228318886aa75da6ae397f74d1801d40295e8
@bang9 bang9 force-pushed the cherrypick-0.72-fix-virtualized-list-for-mvcp branch from d9e02ff to 96d7c92 Compare May 4, 2023 18:24
@bang9 bang9 changed the title [0.72] Cherry pick - Fix VirtualizedList with maintainVisibleContentPosition#35993 [0.72] Cherry pick - VirtualizedList fixes May 4, 2023
Copy link
Contributor

@NickGerleman NickGerleman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#35993 is quite a large change but we're probably fine, since it has been running internally for a few weeks, and we haven't released 0.72 yet.

@kelset kelset merged commit 42fc885 into facebook:0.72-stable May 22, 2023
@bang9 bang9 deleted the cherrypick-0.72-fix-virtualized-list-for-mvcp branch May 23, 2023 01:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Pick Request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants