Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51785
Adds `flow` to the remaining files that are lacking it in the `packages/virtualized-lists` directory. In one of the Jest tests, there are so many failures that for now, I just added `noflow`.
Changelog:
[Internal]
Reviewed By: SamChou19815
Differential Revision: D75888514
fbshipit-source-id: 29d96292f3d59fd5cf2f5ba09b58fdfb9eabab2e
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51409
Prefers using this as a destructured import instead of as a member expression of `React`.
Changelog:
[Internal]
Reviewed By: SamChou19815
Differential Revision: D74895837
fbshipit-source-id: b9d6082e4882d95f0d2aa1eed13b725edeb854cd
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51399
Prefers using this as a destructured import instead of as a member expression of `React`.
Changelog:
[Internal]
Reviewed By: rubennorte
Differential Revision: D74888097
fbshipit-source-id: a22ca4b791153ff0c2f4ab34ff8e3ce5e9280e0d
Summary:
Fixes https://github.com/facebook/react-native/issues/50817
Using a fragment is very common when rendering elements. We are cloning and adding `onLayout` always to the ListEmptyComponent element, but this would seem to work only when `View` is used for this as a wrapper in this prop. To prevent this unnecessary warning, I think we can easily check whether it is a fragment or not before cloning and adding the extra props – this adds backwards compatibility for those that don't need to use `onLayout`.
## Changelog:
[GENERAL] [FIXED] - Skip cloning Fragments in ListEmptyComponent to avoid onLayout warning
Pull Request resolved: https://github.com/facebook/react-native/pull/50833
Test Plan: Use the code snippet from the linked issue to verify that the warning is not thrown anymore when using a Fragment.
Reviewed By: javache
Differential Revision: D73421503
Pulled By: rshest
fbshipit-source-id: 0da4a38130601943e4704589ac275eba39767191
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49262
Changelog: [General][Breaking] Deep imports into `react-native/virtualized-lists` with require syntax may need to be appended with `.default`
Reviewed By: huntie
Differential Revision: D69308532
fbshipit-source-id: 6de15d46e0931616bc9849edbccb7cf745e15dd5
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48819
Currently, `VirtualizedList-test.js` has a subtle dependency on how asynchronous operations are queued. Specifically, it depends on...
- `Batchinator` to use `setTimeout` for...
- `InteractionManager` to use `setImmediate` for...
- `InteractionManager` to resolve a promise via microtask.
As a consequence, any changes to this queueing logic (e.g. eliminating the unnecessary `setImmediate` and microtask) unnecessarily breaks these unit tests.
This changes the Jest unit tests to instead use `jest. advanceTimersToNextTimer(<step>)` instead of `jest.runOnlyPendingTimers()` so that the unit tests are no longer dependent on these specific queueing logic.
Changelog:
[Internal]
Reviewed By: NickGerleman
Differential Revision: D68449850
fbshipit-source-id: 382b1c0a0d8fade873ccf17a9deb3622a83b8163
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47965
Changelog: [General] [Changed] - fix item disappearing with scroll in VirtualizedList
It was caused because the function `computeWindowedRenderLimits` collapsed the current window size to just 1 element. So, users start scroll current window increase from {left:0, right:5 } -> {left:0, right:6 } and after some edge cases the window collapsed to `{left:6, right:6 }` which cause to remove all elements and recreate them later. As a result users have a lot of lags and blank pages.
The diff fixes the collapsing window size to 1 element. Also fix other decreasing `left` position even if windowSize more than current amount of elements.
Reviewed By: NickGerleman
Differential Revision: D66334188
fbshipit-source-id: 2162d00d03d64ab6325c0492d87449051e68a4e9
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46990
This is a reattempt land D63643856 to fix the scroll onendreached event not firing.
Changelog:
[General][Fixed] - Fix onEndReached not being called when getItemLayout is present and we scroll past render window
Reviewed By: NickGerleman
Differential Revision: D64222424
fbshipit-source-id: 7e22f377d2f754beb39fff2b5c097cea350daa7e
Summary:
This diff reverts D63643856
`_highestMeasuredFrameIndex` is not properly invalidated causing issues with previous logic when data size shrinks.
bypass-github-export-checks
Changelog:
[General][Changed] - Revert "Fix onEndReached not being called when getItemLayout is present and we scroll past render window"
Reviewed By: NickGerleman
Differential Revision: D64009287
fbshipit-source-id: 8a21b57f5247fc743e65f9a730ff33a9a89d2bc1
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46736
Copying the comment in code:
> We only call `onEndReached` after we render the last cell, but when getItemLayout is present, we can scroll past the last rendered cell, and never trigger a new layout or bounds change, so we need to check again after rendering more cells.
Changelog:
[General][Fixed] - Fix onEndReached not being called when getItemLayout is present and we scroll past render window
Reviewed By: yungsters
Differential Revision: D63643856
fbshipit-source-id: 9c53789cb15b225ceac353c37cbb67f7beeaf4fb
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46487
Changelog: [General][Fixed] Fixed accuracy of FlatList estimations to determine what elements are visible in the rendering window.
This fixes a bug in FlatList where it thinks that some elements are visible in the rendering window, when they're not. Specifically, if a cell hasn't been laid out yet, it ignores all the information it already has on the ones that had, and estimates its position and offset based on the estimated size of the cells. In this case, if the first element has a larger offset because the list has a header, that offset is ignored in this case.
One observed result of this is that in a list where there's a header and a single cell that occupy the whole rendering window, FlatList thinks it needs to pre-render an additional element because the header is ignored.
Reviewed By: NickGerleman
Differential Revision: D62649060
fbshipit-source-id: 437bae79916707ca1d08784190508a9f7e36688e
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/45002
There are a couple Jest unit test cases for `VirtualizedList-test.js` that require further investigation.
We believe that these are problems with Jest fake timers in the test and not with the component itself, so for now let's skip them so as to unblock the upgrade to React 19.
Changelog:
[Internal]
Reviewed By: robhogan
Differential Revision: D58656948
fbshipit-source-id: d52f3ad8277def6eae20cbbc11751d73b769d929
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/44997
Wrap `VirtualizedList-test` uses of `react-test-renderer` in `act` as appropriate, so as to pass under current React and mostly pass under React 19, with further fixes to come.
Changelog: [Internal]
Reviewed By: robhogan
Differential Revision: D58649295
fbshipit-source-id: 5e0fa791d581fbf004a2ca7eaa5c4b4d9a15ddfe
Summary:
Migrate `VirtualizedSectionList-test` to use `act`-wrapping in prepation for React 19.
Avoid the `react-native/jest/renderer` abstractions as this is a separate package.
Changelog: [Internal]
Reviewed By: yungsters
Differential Revision: D58653558
fbshipit-source-id: 0e19fff5c3998fb00b71c3d07100a2064682cb4c
Summary:
Stacked on top of #28498 for test fixes.
### Don't Rethrow
When we started React it was 1:1 setState calls a series of renders and
if they error, it errors where the setState was called. Simple. However,
then batching came and the error actually got thrown somewhere else.
With concurrent mode, it's not even possible to get setState itself to
throw anymore.
In fact, all APIs that can rethrow out of React are executed either at
the root of the scheduler or inside a DOM event handler.
If you throw inside a React.startTransition callback that's sync, then
that will bubble out of the startTransition but if you throw inside an
async callback or a useTransition we now need to handle it at the hook
site. So in 19 we need to make all React.startTransition swallow the
error (and report them to reportError).
The only one remaining that can throw is flushSync but it doesn't really
make sense for it to throw at the callsite neither because batching.
Just because something rendered in this flush doesn't mean it was
rendered due to what was just scheduled and doesn't mean that it should
abort any of the remaining code afterwards. setState is fire and forget.
It's send an instruction elsewhere, it's not part of the current
imperative code.
Error boundaries never rethrow. Since you should really always have
error boundaries, most of the time, it wouldn't rethrow anyway.
Rethrowing also actually currently drops errors on the floor since we
can only rethrow the first error, so to avoid that we'd need to call
reportError anyway. This happens in RN events.
The other issue with rethrowing is that it logs an extra console.error.
Since we're not sure that user code will actually log it anywhere we
still log it too just like we do with errors inside error boundaries
which leads all of these to log twice.
The goal of this PR is to never rethrow out of React instead, errors
outside of error boundaries get logged to reportError. Event system
errors too.
### Breaking Changes
The main thing this affects is testing where you want to inspect the
errors thrown. To make it easier to port, if you're inside `act` we
track the error into act in an aggregate error and then rethrow it at
the root of `act`. Unlike before though, if you flush synchronously
inside of act it'll still continue until the end of act before
rethrowing.
I expect most user code breakages would be to migrate from `flushSync`
to `act` if you assert on throwing.
However, in the React repo we also have `internalAct` and the
`waitForThrow` helpers. Since these have to use public production
implementations we track these using the global onerror or process
uncaughtException. Unlike regular act, includes both event handler
errors and onRecoverableError by default too. Not just render/commit
errors. So I had to account for that in our tests.
We restore logging an extra log for uncaught errors after the main log
with the component stack in it. We use `console.warn`. This is not yet
ignorable if you preventDefault to the main error event. To avoid
confusion if you don't end up logging the error to console I just added
`An error occurred`.
### Polyfill
All browsers we support really supports `reportError` but not all test
and server environments do, so I implemented a polyfill for browser and
node in `shared/reportGlobalError`. I don't love that this is included
in all builds and gets duplicated into isomorphic even though it's not
actually needed in production. Maybe in the future we can require a
polyfill for this.
### Follow Ups
In a follow up, I'll make caught vs uncaught error handling be
configurable too.
---------
DiffTrain build for commit https://github.com/facebook/react/commit/6786563f3cbbc9b16d5a8187207b5bd904386e53.
Changelog:
[Internal]
Reviewed By: kassens
Differential Revision: D55408481
Pulled By: yungsters
fbshipit-source-id: 598aa306369e21cb3e93ad6041a87bfbaa9eef9e
Co-authored-by: Ricky Hanlon <rickhanlonii@gmail.com>
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/43458
The React sync in D54783723 was failing because some tests were relying on `console.error` being called as `console.error('Some message')` but it was refactored as `console.error('%s..', 'Some message')`, making them fail.
This fixes the tests by formatting the arguments passed to the console functions and checking against that instead.
Changelog: [internal]
Reviewed By: sammy-SC
Differential Revision: D54849485
fbshipit-source-id: 0648263614725ea3f9c95b9f9bb13005adae46eb
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/43377
Fixes `VirtualizedList-test.js`, which assumes fake timers (e.g. using `jest.runAllTimers()` and `jest.runOnlyPendingTimers()`) but did not actually use fake timers.
Changelog:
[Internal]
Reviewed By: yungsters
Differential Revision: D54668281
fbshipit-source-id: b14757744bb7a21a4e5573053549c36178826021
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/41113
changelog: [internal]
We must prevent VirtualizedList._onContentSizeChange from being triggered by a conflicting bubbling onContentSizeChange event.
For TextInput, we change the event onContentSizeChange from bubbling to direct (https://github.com/facebook/react-native/commit/744fb4a0d23d15a40cd591e31f6c0f6cb3a7f06b). To make this safer, we need to filter out any `onContentSizeChange` event since we can't control 3rd party components from dispatching onContentSizeChange as bubbling event.
Reviewed By: NickGerleman
Differential Revision: D50451232
fbshipit-source-id: b7a446e4efc9c45024d37f35cb53f2fcbb28799f
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/41051
Strictifies flow to flow strict-local in files where doing that doesn't cause new flow errors.
Changelog: Internal
Reviewed By: yungsters
Differential Revision: D50369011
fbshipit-source-id: b4a5a26b839b7327a3178e6f5b35246dea365a38
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/39646
We can dramatically simplify this code and remove quirks/hacks, now that we can assume layout events are always fired top down.
Changelog: [Internal]
Reviewed By: yungsters
Differential Revision: D49628669
fbshipit-source-id: 7de5bbc4597eba1c59aaa7672c70e76d2786c7ef
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/38732
Unit tests for cell offset APIs. Mostly the same as previous UTs for VirtualizedList a layer higher.
Changelog: [Internal]
Reviewed By: rozele
Differential Revision: D47978633
fbshipit-source-id: 8cb8a2e8125bc7370eabf9f01a3f7529043171c2
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/38936
Unit tests that validate we continue to return correct measurement results through the lifetime of the list when cells are unmounted, or layout of the list shifts.
Changelog: [Internal]
Reviewed By: lenaic
Differential Revision: D47978632
fbshipit-source-id: b700fafb699e8820de6825f0ead1ef02c6d8f168
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/38962
Changelog: [General][Changed] Math.floor the top and bottom dimensions of a cell item when determining viewability.
Reviewed By: NickGerleman
Differential Revision: D48212402
fbshipit-source-id: 0ba7d5c218477c257a4504391940d916e4832f91
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/38735
UTs around cell measurement results through `getCellMetrics` and `getCellMetricsApprox`. For each orientation, validate basic scenarios for approximation, cached measurement, or measurement by user-provided `getItemLayout`.
Changelog: [Internal]
Reviewed By: rozele
Differential Revision: D47978630
fbshipit-source-id: 4c9ba6a60599848034f4f23cde3497dd4c2b8788
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/38648https://github.com/facebook/react-native/pull/38475 made this code no longer no-op on Android, which caused regressions documented in https://github.com/facebook/react-native/issues/38470#issuecomment-1639620459 due to VirtualizedList having more out-of-date information.
We are already coalescing scroll events on both Android and iOS, which will ensure we are not flooded with events. VirtualizedList also already inserts an artificial 50ms delay to new renders by default when high priority work is not happening (see `updateCellsBatchingPeriod`). This limits the heavy work done by VirtualizedList (no new renders or expensive math on scroll events), while letting the list still have the most recent events.
We can eventually remove this once VirtualizedList is able to use OffScreen universally.
Changelog:
[General][Changed] - Remove default 50ms Scroll Event Throttling in VirtualizedList
Reviewed By: ryancat
Differential Revision: D47823772
fbshipit-source-id: 55d22a1074235ccc1b2cf167f6b1758640c79edb
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/38655https://github.com/facebook/react-native/pull/35993 added logic in VirtualizedList to support `maintainVisibleContentPosition`. This logic makes sure that a previously visible cell being used as an anchor remains rendered after new content is added.
The strategy here is to calculate the difference in previous and new positions of the anchor, and move the render window to its new location during item change. `minIndexForVisible` is used as this anchor.
When an item change moves the anchor to a position below `minIndexForVisible`, shifting the render window may result in a window which starts before zero. This fixes up `_constrainToItemCount()` to handle this.
Changelog:
[General][Fixed] - Fix invariant violation when `maintainVisibleContentPosition` adjustment moves window before list start
Reviewed By: yungsters
Differential Revision: D47846165
fbshipit-source-id: 8a36f66fdad321acb255745dad85618d28c54dba
Summary:
This PR is a result of this PR, which got merged but then reverted:
- https://github.com/facebook/react-native/pull/37913
We are trying to implement a workaround for https://github.com/facebook/react-native/issues/35350, so react-native users on android API 33+ can use `<FlatList inverted={true} />` without running into ANRs.
As explained in the issue, starting from android API 33 there are severe performance issues when using scaleY: -1 on a view, and its child view, which is what we are doing when inverting the ScrollView component (e.g. in FlatList).
This PR adds a workaround. The workaround is to also scale on the X-Axis which causes a different transform matrix to be created, that doesn't cause the ANR (see the issue for details).
However, when doing that the vertical scroll bar will be on the wrong side, thus we switch the position in the native code once we detect that the list is inverted, using the newly added `isInvertedVirtualizedList` prop.
This is a follow up PR to:
- https://github.com/facebook/react-native/pull/38071⚠️ **Note:** [38071](https://github.com/facebook/react-native/pull/38071) needs to be merged and shipped first! Only then we can merge this PR.
## Changelog:
<!-- Help reviewers and the release process by writing your own changelog entry.
Pick one each for the category and type tags:
[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message
For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->
[ANDROID] [FIXED] - ANR when having an inverted FlatList on android API 33+
Pull Request resolved: https://github.com/facebook/react-native/pull/38073
Test Plan:
- Check the RN tester app and see that scrollview is still working as expected
- Add the `internalAndroidApplyInvertedFix` prop as test to a scrollview and see how the scrollbar will change position.
Reviewed By: cortinico
Differential Revision: D47848063
Pulled By: NickGerleman
fbshipit-source-id: 4a6948a8b89f0b39f01b7a2d44dba740c53fabb3
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/37777
This extracts the state and logic VirtualizedList uses to query information related to cell metrics. We will need to modify this (and other places) when fixing up RTL support for horizontal FlatList.
Changelog: [Internal]
Reviewed By: javache
Differential Revision: D46427052
fbshipit-source-id: 0a23f6c726447de0f20c583b4d507003efd6a754
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/37778
These functions describe how to get the metrics of a specific cell. "Frame" here seems non-descriptive and redundant to the "Metrics" part. Renaming this before a larger refactor.
Changelog: [Internal]
Reviewed By: philIip
Differential Revision: D46427058
fbshipit-source-id: e9ad9cf15e1adfd07604eb11526de0ed8cf99000
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 https://github.com/facebook/react-native/issues/2, after new content is added https://github.com/facebook/react-native/issues/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 https://github.com/facebook/react-native/issues/2 is no longer rendered so there's no way to maintain its position.
#### Illustration 1

#### Illustration 2

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: https://github.com/facebook/react-native/pull/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 https://github.com/facebook/react-native/pull/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
Summary:
Hey,
`adjustCellsAroundViewport` function was checking if `props.initialScrollIndex` is truthy and -1 was returning true. This caused bugs with rendering for tvOS: https://github.com/react-native-tvos/react-native-tvos/pull/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: https://github.com/facebook/react-native/pull/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
Summary:
This change reverts D41745930 (https://github.com/facebook/react-native/commit/2e3dbe9c2fbff52448e2d5a7c1e4c96b1016cf25) as part of a stack to splice back source history which was lost (Git registered the file moves as additions).
It is expected this diff will individually fail. The entire stack should be applied at once.
Changelog: [Internal]
Reviewed By: hoxyq
Differential Revision: D43068113
fbshipit-source-id: c8398629fe5dcc1ca4bf02f550adc00c78a8487a