@shopify/flash-list 2.0.0-alpha.9 → 2.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -97
- package/android/src/main/kotlin/com/shopify/reactnative/flash_list/BlankAreaEvent.kt +2 -2
- package/dist/AnimatedFlashList.d.ts.map +1 -1
- package/dist/AnimatedFlashList.js +3 -3
- package/dist/AnimatedFlashList.js.map +1 -1
- package/dist/FlashList.d.ts +9 -0
- package/dist/FlashList.d.ts.map +1 -1
- package/dist/FlashList.js +20 -0
- package/dist/FlashList.js.map +1 -1
- package/dist/FlashListProps.d.ts +30 -10
- package/dist/FlashListProps.d.ts.map +1 -1
- package/dist/FlashListProps.js.map +1 -1
- package/dist/FlashListRef.d.ts +305 -0
- package/dist/FlashListRef.d.ts.map +1 -0
- package/dist/FlashListRef.js +3 -0
- package/dist/FlashListRef.js.map +1 -0
- package/dist/MasonryFlashList.js.map +1 -1
- package/dist/__tests__/RecyclerView.test.js +72 -28
- package/dist/__tests__/RecyclerView.test.js.map +1 -1
- package/dist/__tests__/RenderStackManager.test.d.ts +2 -0
- package/dist/__tests__/RenderStackManager.test.d.ts.map +1 -0
- package/dist/__tests__/RenderStackManager.test.js +485 -0
- package/dist/__tests__/RenderStackManager.test.js.map +1 -0
- package/dist/__tests__/helpers/createLayoutManager.d.ts.map +1 -1
- package/dist/__tests__/helpers/createLayoutManager.js +3 -4
- package/dist/__tests__/helpers/createLayoutManager.js.map +1 -1
- package/dist/__tests__/useUnmountAwareCallbacks.test.js +1 -1
- package/dist/__tests__/useUnmountAwareCallbacks.test.js.map +1 -1
- package/dist/benchmark/useBenchmark.js +0 -25
- package/dist/benchmark/useBenchmark.js.map +1 -1
- package/dist/benchmark/useFlatListBenchmark.js +8 -7
- package/dist/benchmark/useFlatListBenchmark.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/native/config/PlatformHelper.android.d.ts +1 -0
- package/dist/native/config/PlatformHelper.android.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.android.js +1 -0
- package/dist/native/config/PlatformHelper.android.js.map +1 -1
- package/dist/native/config/PlatformHelper.d.ts +1 -0
- package/dist/native/config/PlatformHelper.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.ios.d.ts +1 -0
- package/dist/native/config/PlatformHelper.ios.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.ios.js +1 -0
- package/dist/native/config/PlatformHelper.ios.js.map +1 -1
- package/dist/native/config/PlatformHelper.js +1 -0
- package/dist/native/config/PlatformHelper.js.map +1 -1
- package/dist/native/config/PlatformHelper.web.d.ts +1 -0
- package/dist/native/config/PlatformHelper.web.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.web.js +1 -0
- package/dist/native/config/PlatformHelper.web.js.map +1 -1
- package/dist/recyclerview/RecyclerView.d.ts +2 -1
- package/dist/recyclerview/RecyclerView.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerView.js +104 -57
- package/dist/recyclerview/RecyclerView.js.map +1 -1
- package/dist/recyclerview/RecyclerViewContextProvider.d.ts +41 -6
- package/dist/recyclerview/RecyclerViewContextProvider.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerViewContextProvider.js +4 -0
- package/dist/recyclerview/RecyclerViewContextProvider.js.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.d.ts +24 -7
- package/dist/recyclerview/RecyclerViewManager.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.js +119 -113
- package/dist/recyclerview/RecyclerViewManager.js.map +1 -1
- package/dist/recyclerview/RenderStackManager.d.ts +86 -0
- package/dist/recyclerview/RenderStackManager.d.ts.map +1 -0
- package/dist/recyclerview/RenderStackManager.js +343 -0
- package/dist/recyclerview/RenderStackManager.js.map +1 -0
- package/dist/recyclerview/ViewHolder.d.ts.map +1 -1
- package/dist/recyclerview/ViewHolder.js +5 -3
- package/dist/recyclerview/ViewHolder.js.map +1 -1
- package/dist/recyclerview/ViewHolderCollection.d.ts +9 -3
- package/dist/recyclerview/ViewHolderCollection.d.ts.map +1 -1
- package/dist/recyclerview/ViewHolderCollection.js +26 -9
- package/dist/recyclerview/ViewHolderCollection.js.map +1 -1
- package/dist/recyclerview/components/ScrollAnchor.d.ts +2 -2
- package/dist/recyclerview/components/ScrollAnchor.d.ts.map +1 -1
- package/dist/recyclerview/components/ScrollAnchor.js +9 -5
- package/dist/recyclerview/components/ScrollAnchor.js.map +1 -1
- package/dist/recyclerview/components/StickyHeaders.d.ts +1 -1
- package/dist/recyclerview/components/StickyHeaders.d.ts.map +1 -1
- package/dist/recyclerview/components/StickyHeaders.js +40 -33
- package/dist/recyclerview/components/StickyHeaders.js.map +1 -1
- package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts +45 -1
- package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts.map +1 -1
- package/dist/recyclerview/helpers/EngagedIndicesTracker.js +77 -20
- package/dist/recyclerview/helpers/EngagedIndicesTracker.js.map +1 -1
- package/dist/recyclerview/helpers/RenderTimeTracker.d.ts +11 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.d.ts.map +1 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.js +42 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.js.map +1 -0
- package/dist/recyclerview/helpers/VelocityTracker.d.ts +29 -0
- package/dist/recyclerview/helpers/VelocityTracker.d.ts.map +1 -0
- package/dist/recyclerview/helpers/VelocityTracker.js +70 -0
- package/dist/recyclerview/helpers/VelocityTracker.js.map +1 -0
- package/dist/recyclerview/hooks/useBoundDetection.d.ts +1 -2
- package/dist/recyclerview/hooks/useBoundDetection.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useBoundDetection.js +56 -22
- package/dist/recyclerview/hooks/useBoundDetection.js.map +1 -1
- package/dist/recyclerview/hooks/useLayoutState.d.ts +3 -1
- package/dist/recyclerview/hooks/useLayoutState.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useLayoutState.js +5 -3
- package/dist/recyclerview/hooks/useLayoutState.js.map +1 -1
- package/dist/recyclerview/hooks/useMappingHelper.d.ts +1 -1
- package/dist/recyclerview/hooks/useMappingHelper.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useMappingHelper.js +1 -1
- package/dist/recyclerview/hooks/useMappingHelper.js.map +1 -1
- package/dist/recyclerview/hooks/useOnLoad.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useOnLoad.js +4 -6
- package/dist/recyclerview/hooks/useOnLoad.js.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.d.ts +5 -49
- package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.js +315 -204
- package/dist/recyclerview/hooks/useRecyclerViewController.js.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts +2 -0
- package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewManager.js +11 -1
- package/dist/recyclerview/hooks/useRecyclerViewManager.js.map +1 -1
- package/dist/recyclerview/hooks/useRecyclingState.d.ts +4 -2
- package/dist/recyclerview/hooks/useRecyclingState.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclingState.js +2 -2
- package/dist/recyclerview/hooks/useRecyclingState.js.map +1 -1
- package/dist/recyclerview/hooks/useSecondaryProps.js +1 -1
- package/dist/recyclerview/hooks/useUnmountAwareCallbacks.d.ts +10 -3
- package/dist/recyclerview/hooks/useUnmountAwareCallbacks.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js +33 -4
- package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js.map +1 -1
- package/dist/recyclerview/hooks/useUnmountFlag.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useUnmountFlag.js +1 -0
- package/dist/recyclerview/hooks/useUnmountFlag.js.map +1 -1
- package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts +18 -4
- package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/GridLayoutManager.js +60 -21
- package/dist/recyclerview/layout-managers/GridLayoutManager.js.map +1 -1
- package/dist/recyclerview/layout-managers/LayoutManager.d.ts +35 -21
- package/dist/recyclerview/layout-managers/LayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/LayoutManager.js +92 -28
- package/dist/recyclerview/layout-managers/LayoutManager.js.map +1 -1
- package/dist/recyclerview/layout-managers/MasonryLayoutManager.d.ts +9 -1
- package/dist/recyclerview/layout-managers/MasonryLayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/MasonryLayoutManager.js +28 -12
- package/dist/recyclerview/layout-managers/MasonryLayoutManager.js.map +1 -1
- package/dist/recyclerview/utils/measureLayout.web.d.ts.map +1 -1
- package/dist/recyclerview/utils/measureLayout.web.js +1 -3
- package/dist/recyclerview/utils/measureLayout.web.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/viewability/ViewToken.d.ts +2 -2
- package/dist/viewability/ViewToken.d.ts.map +1 -1
- package/dist/viewability/ViewabilityHelper.js +1 -1
- package/dist/viewability/ViewabilityHelper.js.map +1 -1
- package/dist/viewability/ViewabilityManager.d.ts.map +1 -1
- package/dist/viewability/ViewabilityManager.js +11 -5
- package/dist/viewability/ViewabilityManager.js.map +1 -1
- package/jestSetup.js +30 -11
- package/package.json +2 -1
- package/src/AnimatedFlashList.ts +3 -2
- package/src/FlashList.tsx +24 -0
- package/src/FlashListProps.ts +41 -10
- package/src/FlashListRef.ts +320 -0
- package/src/MasonryFlashList.tsx +2 -2
- package/src/__tests__/RecyclerView.test.tsx +106 -31
- package/src/__tests__/RenderStackManager.test.ts +574 -0
- package/src/__tests__/helpers/createLayoutManager.ts +2 -3
- package/src/__tests__/useUnmountAwareCallbacks.test.tsx +12 -12
- package/src/benchmark/useBenchmark.ts +0 -37
- package/src/benchmark/useFlatListBenchmark.ts +2 -2
- package/src/index.ts +2 -1
- package/src/native/config/PlatformHelper.android.ts +1 -0
- package/src/native/config/PlatformHelper.ios.ts +1 -0
- package/src/native/config/PlatformHelper.ts +1 -0
- package/src/native/config/PlatformHelper.web.ts +1 -0
- package/src/recyclerview/RecyclerView.tsx +139 -75
- package/src/recyclerview/RecyclerViewContextProvider.ts +52 -7
- package/src/recyclerview/RecyclerViewManager.ts +135 -98
- package/src/recyclerview/RenderStackManager.ts +317 -0
- package/src/recyclerview/ViewHolder.tsx +5 -3
- package/src/recyclerview/ViewHolderCollection.tsx +42 -14
- package/src/recyclerview/components/ScrollAnchor.tsx +21 -9
- package/src/recyclerview/components/StickyHeaders.tsx +63 -45
- package/src/recyclerview/helpers/EngagedIndicesTracker.ts +118 -23
- package/src/recyclerview/helpers/RenderTimeTracker.ts +42 -0
- package/src/recyclerview/helpers/VelocityTracker.ts +77 -0
- package/src/recyclerview/hooks/useBoundDetection.ts +72 -23
- package/src/recyclerview/hooks/useLayoutState.ts +15 -6
- package/src/recyclerview/hooks/useMappingHelper.ts +1 -1
- package/src/recyclerview/hooks/useOnLoad.ts +4 -6
- package/src/recyclerview/hooks/useRecyclerViewController.tsx +364 -254
- package/src/recyclerview/hooks/useRecyclerViewManager.ts +13 -1
- package/src/recyclerview/hooks/useRecyclingState.ts +11 -7
- package/src/recyclerview/hooks/useSecondaryProps.tsx +1 -1
- package/src/recyclerview/hooks/useUnmountAwareCallbacks.ts +39 -3
- package/src/recyclerview/hooks/useUnmountFlag.ts +1 -0
- package/src/recyclerview/layout-managers/GridLayoutManager.ts +67 -23
- package/src/recyclerview/layout-managers/LayoutManager.ts +110 -41
- package/src/recyclerview/layout-managers/MasonryLayoutManager.ts +30 -8
- package/src/recyclerview/utils/measureLayout.web.ts +1 -3
- package/src/viewability/ViewToken.ts +2 -2
- package/src/viewability/ViewabilityHelper.ts +1 -1
- package/src/viewability/ViewabilityManager.ts +16 -9
- package/dist/__tests__/RecycleKeyManager.test.d.ts +0 -2
- package/dist/__tests__/RecycleKeyManager.test.d.ts.map +0 -1
- package/dist/__tests__/RecycleKeyManager.test.js +0 -210
- package/dist/__tests__/RecycleKeyManager.test.js.map +0 -1
- package/dist/recyclerview/RecycleKeyManager.d.ts +0 -82
- package/dist/recyclerview/RecycleKeyManager.d.ts.map +0 -1
- package/dist/recyclerview/RecycleKeyManager.js +0 -135
- package/dist/recyclerview/RecycleKeyManager.js.map +0 -1
- package/src/__tests__/RecycleKeyManager.test.ts +0 -254
- package/src/recyclerview/RecycleKeyManager.ts +0 -185
|
@@ -199,42 +199,5 @@ function computeSuggestions(
|
|
|
199
199
|
`Data count is low. Try to increase it to a large number (e.g 200) using the 'useDataMultiplier' hook.`
|
|
200
200
|
);
|
|
201
201
|
}
|
|
202
|
-
const distanceFromWindow = roundToDecimalPlaces(
|
|
203
|
-
flashListRef.current.firstItemOffset,
|
|
204
|
-
0
|
|
205
|
-
);
|
|
206
|
-
if (
|
|
207
|
-
(flashListRef.current.props.estimatedFirstItemOffset || 0) !==
|
|
208
|
-
distanceFromWindow
|
|
209
|
-
) {
|
|
210
|
-
suggestions.push(
|
|
211
|
-
`estimatedFirstItemOffset can be set to ${distanceFromWindow}`
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
const rlv = flashListRef.current.recyclerlistview_unsafe;
|
|
215
|
-
const horizontal = flashListRef.current.props.horizontal;
|
|
216
|
-
if (rlv) {
|
|
217
|
-
const sizeArray = rlv.props.dataProvider
|
|
218
|
-
.getAllData()
|
|
219
|
-
.map((_, index) =>
|
|
220
|
-
horizontal
|
|
221
|
-
? rlv.getLayout?.(index)?.width || 0
|
|
222
|
-
: rlv.getLayout?.(index)?.height || 0
|
|
223
|
-
);
|
|
224
|
-
const averageSize = Math.round(
|
|
225
|
-
sizeArray.reduce((prev, current) => prev + current, 0) /
|
|
226
|
-
sizeArray.length
|
|
227
|
-
);
|
|
228
|
-
if (
|
|
229
|
-
Math.abs(
|
|
230
|
-
averageSize -
|
|
231
|
-
(flashListRef.current.props.estimatedItemSize ??
|
|
232
|
-
flashListRef.current.state.layoutProvider
|
|
233
|
-
.defaultEstimatedItemSize)
|
|
234
|
-
) > 5
|
|
235
|
-
) {
|
|
236
|
-
suggestions.push(`estimatedItemSize can be set to ${averageSize}`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
202
|
}
|
|
240
203
|
}
|
|
@@ -25,7 +25,7 @@ export function useFlatListBenchmark(
|
|
|
25
25
|
) {
|
|
26
26
|
useEffect(() => {
|
|
27
27
|
const cancellable = new Cancellable();
|
|
28
|
-
if (flatListRef.current) {
|
|
28
|
+
if (flatListRef.current && flatListRef.current.props) {
|
|
29
29
|
if (!(Number(flatListRef.current.props.data?.length) > 0)) {
|
|
30
30
|
throw new Error("Data is empty, cannot run benchmark");
|
|
31
31
|
}
|
|
@@ -71,7 +71,7 @@ async function runScrollBenchmark(
|
|
|
71
71
|
scrollSpeedMultiplier: number
|
|
72
72
|
): Promise<void> {
|
|
73
73
|
if (flatListRef.current) {
|
|
74
|
-
const horizontal = flatListRef.current.props
|
|
74
|
+
const horizontal = Boolean(flatListRef.current.props?.horizontal);
|
|
75
75
|
|
|
76
76
|
const fromX = 0;
|
|
77
77
|
const fromY = 0;
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { RecyclerView } from "./recyclerview/RecyclerView";
|
|
|
5
5
|
|
|
6
6
|
// Keep this unmodified for TS type checking
|
|
7
7
|
export { default as FlashList } from "./FlashList";
|
|
8
|
+
export { FlashListRef } from "./FlashListRef";
|
|
8
9
|
export {
|
|
9
10
|
FlashListProps,
|
|
10
11
|
ContentStyle,
|
|
@@ -52,7 +53,7 @@ export { default as ViewToken } from "./viewability/ViewToken";
|
|
|
52
53
|
export { default as CellContainer } from "./native/cell-container/CellContainer";
|
|
53
54
|
export { RecyclerView } from "./recyclerview/RecyclerView";
|
|
54
55
|
export { RecyclerViewProps } from "./recyclerview/RecyclerViewProps";
|
|
55
|
-
export {
|
|
56
|
+
export { useFlashListContext } from "./recyclerview/RecyclerViewContextProvider";
|
|
56
57
|
|
|
57
58
|
// @ts-ignore - This is ignored by TypeScript but will be present in the compiled JS
|
|
58
59
|
// In the compiled JS, this will override the previous FlashList export with a conditional one
|
|
@@ -3,6 +3,7 @@ import { BaseItemAnimator } from "recyclerlistview";
|
|
|
3
3
|
const PlatformConfig = {
|
|
4
4
|
defaultDrawDistance: 250,
|
|
5
5
|
supportsOffsetCorrection: true,
|
|
6
|
+
trackAverageRenderTimeForOffsetProjection: true,
|
|
6
7
|
// Using rotate instead of scaleY on Android to avoid performance issues. Issue: https://github.com/Shopify/flash-list/issues/751
|
|
7
8
|
invertedTransformStyle: { transform: [{ rotate: "180deg" }] },
|
|
8
9
|
invertedTransformStyleHorizontal: { transform: [{ rotate: "180deg" }] },
|
|
@@ -3,6 +3,7 @@ import { BaseItemAnimator } from "recyclerlistview";
|
|
|
3
3
|
const PlatformConfig = {
|
|
4
4
|
defaultDrawDistance: 250,
|
|
5
5
|
supportsOffsetCorrection: true,
|
|
6
|
+
trackAverageRenderTimeForOffsetProjection: false,
|
|
6
7
|
invertedTransformStyle: { transform: [{ scaleY: -1 }] },
|
|
7
8
|
invertedTransformStyleHorizontal: { transform: [{ scaleX: -1 }] },
|
|
8
9
|
};
|
|
@@ -4,6 +4,7 @@ import { DefaultJSItemAnimator } from "recyclerlistview/dist/reactnative/platfor
|
|
|
4
4
|
const PlatformConfig = {
|
|
5
5
|
defaultDrawDistance: 250,
|
|
6
6
|
supportsOffsetCorrection: false,
|
|
7
|
+
trackAverageRenderTimeForOffsetProjection: false,
|
|
7
8
|
invertedTransformStyle: { transform: [{ scaleY: -1 }] },
|
|
8
9
|
invertedTransformStyleHorizontal: { transform: [{ scaleX: -1 }] },
|
|
9
10
|
};
|
|
@@ -6,6 +6,7 @@ import { DefaultJSItemAnimator } from "recyclerlistview/dist/reactnative/platfor
|
|
|
6
6
|
const PlatformConfig = {
|
|
7
7
|
defaultDrawDistance: 500,
|
|
8
8
|
supportsOffsetCorrection: false,
|
|
9
|
+
trackAverageRenderTimeForOffsetProjection: false,
|
|
9
10
|
invertedTransformStyle: { transform: [{ scaleY: -1 }] },
|
|
10
11
|
invertedTransformStyleHorizontal: { transform: [{ scaleX: -1 }] },
|
|
11
12
|
};
|
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
NativeSyntheticEvent,
|
|
20
20
|
} from "react-native";
|
|
21
21
|
|
|
22
|
+
import { FlashListRef } from "../FlashListRef";
|
|
23
|
+
|
|
22
24
|
import { RVDimension } from "./layout-managers/LayoutManager";
|
|
23
25
|
import {
|
|
24
26
|
areDimensionsNotEqual,
|
|
@@ -47,6 +49,7 @@ import { useSecondaryProps } from "./hooks/useSecondaryProps";
|
|
|
47
49
|
import { StickyHeaders, StickyHeaderRef } from "./components/StickyHeaders";
|
|
48
50
|
import { ScrollAnchor, ScrollAnchorRef } from "./components/ScrollAnchor";
|
|
49
51
|
import { useRecyclerViewController } from "./hooks/useRecyclerViewController";
|
|
52
|
+
import { RenderTimeTracker } from "./helpers/RenderTimeTracker";
|
|
50
53
|
|
|
51
54
|
/**
|
|
52
55
|
* Main RecyclerView component that handles list rendering, scrolling, and item recycling.
|
|
@@ -54,7 +57,7 @@ import { useRecyclerViewController } from "./hooks/useRecyclerViewController";
|
|
|
54
57
|
*/
|
|
55
58
|
const RecyclerViewComponent = <T,>(
|
|
56
59
|
props: RecyclerViewProps<T>,
|
|
57
|
-
ref: React.Ref<
|
|
60
|
+
ref: React.Ref<FlashListRef<T>>
|
|
58
61
|
) => {
|
|
59
62
|
// Destructure props and initialize refs
|
|
60
63
|
const {
|
|
@@ -75,8 +78,6 @@ const RecyclerViewComponent = <T,>(
|
|
|
75
78
|
ListFooterComponentStyle,
|
|
76
79
|
ItemSeparatorComponent,
|
|
77
80
|
renderScrollComponent,
|
|
78
|
-
onScroll,
|
|
79
|
-
disableRecycling,
|
|
80
81
|
style,
|
|
81
82
|
stickyHeaderIndices,
|
|
82
83
|
maintainVisibleContentPosition,
|
|
@@ -84,6 +85,10 @@ const RecyclerViewComponent = <T,>(
|
|
|
84
85
|
...rest
|
|
85
86
|
} = props;
|
|
86
87
|
|
|
88
|
+
const [renderTimeTracker] = useState(() => new RenderTimeTracker());
|
|
89
|
+
|
|
90
|
+
renderTimeTracker.startTracking();
|
|
91
|
+
|
|
87
92
|
// Core refs for managing scroll view, internal view, and child container
|
|
88
93
|
const scrollViewRef = useRef<CompatScroller>(null);
|
|
89
94
|
const internalViewRef = useRef<CompatView>(null);
|
|
@@ -109,15 +114,19 @@ const RecyclerViewComponent = <T,>(
|
|
|
109
114
|
);
|
|
110
115
|
|
|
111
116
|
// Initialize core RecyclerView manager and content offset management
|
|
112
|
-
const { recyclerViewManager } =
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
const { recyclerViewManager, velocityTracker } =
|
|
118
|
+
useRecyclerViewManager(props);
|
|
119
|
+
const {
|
|
120
|
+
applyOffsetCorrection,
|
|
121
|
+
computeFirstVisibleIndexForOffsetCorrection,
|
|
122
|
+
applyInitialScrollIndex,
|
|
123
|
+
handlerMethods,
|
|
124
|
+
} = useRecyclerViewController(
|
|
125
|
+
recyclerViewManager,
|
|
126
|
+
ref,
|
|
127
|
+
scrollViewRef,
|
|
128
|
+
scrollAnchorRef
|
|
129
|
+
);
|
|
121
130
|
|
|
122
131
|
// Initialize view holder collection ref
|
|
123
132
|
const viewHolderCollectionRef = useRef<ViewHolderCollectionRef>(null);
|
|
@@ -126,11 +135,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
126
135
|
useOnListLoad(recyclerViewManager, onLoad);
|
|
127
136
|
|
|
128
137
|
// Hook to detect when scrolling reaches list bounds
|
|
129
|
-
const { checkBounds } = useBoundDetection(
|
|
130
|
-
recyclerViewManager,
|
|
131
|
-
props,
|
|
132
|
-
scrollViewRef
|
|
133
|
-
);
|
|
138
|
+
const { checkBounds } = useBoundDetection(recyclerViewManager, scrollViewRef);
|
|
134
139
|
|
|
135
140
|
const isHorizontalRTL = I18nManager.isRTL && horizontal;
|
|
136
141
|
|
|
@@ -176,6 +181,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
176
181
|
* Effect to handle layout updates for list items
|
|
177
182
|
* This ensures proper positioning and recycling of items
|
|
178
183
|
*/
|
|
184
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
179
185
|
useLayoutEffect(() => {
|
|
180
186
|
if (pendingChildIds.size > 0) {
|
|
181
187
|
return;
|
|
@@ -183,7 +189,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
183
189
|
const layoutInfo = Array.from(refHolder, ([index, viewHolderRef]) => {
|
|
184
190
|
const layout = measureItemLayout(
|
|
185
191
|
viewHolderRef.current!,
|
|
186
|
-
recyclerViewManager.
|
|
192
|
+
recyclerViewManager.tryGetLayout(index)
|
|
187
193
|
);
|
|
188
194
|
|
|
189
195
|
// comapre height with stored layout
|
|
@@ -209,7 +215,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
209
215
|
setRenderId((prev) => prev + 1);
|
|
210
216
|
} else {
|
|
211
217
|
viewHolderCollectionRef.current?.commitLayout();
|
|
212
|
-
|
|
218
|
+
applyOffsetCorrection();
|
|
213
219
|
}
|
|
214
220
|
});
|
|
215
221
|
|
|
@@ -221,23 +227,11 @@ const RecyclerViewComponent = <T,>(
|
|
|
221
227
|
if (recyclerViewManager.ignoreScrollEvents) {
|
|
222
228
|
return;
|
|
223
229
|
}
|
|
224
|
-
let velocity = event.nativeEvent.velocity;
|
|
225
230
|
|
|
226
231
|
let scrollOffset = horizontal
|
|
227
232
|
? event.nativeEvent.contentOffset.x
|
|
228
233
|
: event.nativeEvent.contentOffset.y;
|
|
229
234
|
|
|
230
|
-
if (!velocity) {
|
|
231
|
-
const velocityValue =
|
|
232
|
-
recyclerViewManager.getAbsoluteLastScrollOffset() < scrollOffset
|
|
233
|
-
? 1
|
|
234
|
-
: -1;
|
|
235
|
-
velocity = {
|
|
236
|
-
x: horizontal ? velocityValue : 0,
|
|
237
|
-
y: horizontal ? 0 : velocityValue,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
|
|
241
235
|
// Handle RTL (Right-to-Left) layout adjustments
|
|
242
236
|
if (isHorizontalRTL) {
|
|
243
237
|
scrollOffset = adjustOffsetForRTL(
|
|
@@ -245,18 +239,31 @@ const RecyclerViewComponent = <T,>(
|
|
|
245
239
|
event.nativeEvent.contentSize.width,
|
|
246
240
|
event.nativeEvent.layoutMeasurement.width
|
|
247
241
|
);
|
|
248
|
-
if (velocity) {
|
|
249
|
-
velocity = {
|
|
250
|
-
x: -velocity.x,
|
|
251
|
-
y: velocity.y,
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
// Update scroll position and trigger re-render if needed
|
|
256
|
-
if (recyclerViewManager.updateScrollOffset(scrollOffset, velocity)) {
|
|
257
|
-
setRenderId((prev) => prev + 1);
|
|
258
242
|
}
|
|
259
243
|
|
|
244
|
+
velocityTracker.computeVelocity(
|
|
245
|
+
scrollOffset,
|
|
246
|
+
recyclerViewManager.getAbsoluteLastScrollOffset(),
|
|
247
|
+
Boolean(horizontal),
|
|
248
|
+
(velocity, isMomentumEnd) => {
|
|
249
|
+
if (recyclerViewManager.ignoreScrollEvents) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (isMomentumEnd) {
|
|
254
|
+
computeFirstVisibleIndexForOffsetCorrection();
|
|
255
|
+
if (!recyclerViewManager.isOffsetProjectionEnabled) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
recyclerViewManager.resetVelocityCompute();
|
|
259
|
+
}
|
|
260
|
+
// Update scroll position and trigger re-render if needed
|
|
261
|
+
if (recyclerViewManager.updateScrollOffset(scrollOffset, velocity)) {
|
|
262
|
+
setRenderId((prev) => prev + 1);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
|
|
260
267
|
// Update sticky headers and check bounds
|
|
261
268
|
stickyHeaderRef.current?.reportScrollEvent(event.nativeEvent);
|
|
262
269
|
checkBounds();
|
|
@@ -266,22 +273,41 @@ const RecyclerViewComponent = <T,>(
|
|
|
266
273
|
recyclerViewManager.computeItemViewability();
|
|
267
274
|
|
|
268
275
|
// Call user-provided onScroll handler
|
|
269
|
-
onScroll?.(event);
|
|
276
|
+
recyclerViewManager.props.onScroll?.(event);
|
|
270
277
|
},
|
|
271
|
-
[
|
|
278
|
+
[
|
|
279
|
+
checkBounds,
|
|
280
|
+
computeFirstVisibleIndexForOffsetCorrection,
|
|
281
|
+
horizontal,
|
|
282
|
+
isHorizontalRTL,
|
|
283
|
+
recyclerViewManager,
|
|
284
|
+
velocityTracker,
|
|
285
|
+
]
|
|
272
286
|
);
|
|
273
287
|
|
|
288
|
+
const parentRecyclerViewContext = useRecyclerViewContext();
|
|
289
|
+
const recyclerViewId = useId();
|
|
290
|
+
|
|
274
291
|
// Create context for child components
|
|
275
|
-
const recyclerViewContext: RecyclerViewContext = useMemo(() => {
|
|
292
|
+
const recyclerViewContext: RecyclerViewContext<T> = useMemo(() => {
|
|
276
293
|
return {
|
|
277
294
|
layout: () => {
|
|
278
295
|
setLayoutTreeId((prev) => prev + 1);
|
|
279
296
|
},
|
|
280
297
|
getRef: () => {
|
|
281
|
-
|
|
298
|
+
if (recyclerViewManager.isDisposed) {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
return handlerMethods;
|
|
302
|
+
},
|
|
303
|
+
getParentRef: () => {
|
|
304
|
+
return parentRecyclerViewContext?.getRef() ?? null;
|
|
305
|
+
},
|
|
306
|
+
getParentScrollViewRef: () => {
|
|
307
|
+
return parentRecyclerViewContext?.getScrollViewRef() ?? null;
|
|
282
308
|
},
|
|
283
309
|
getScrollViewRef: () => {
|
|
284
|
-
return scrollViewRef;
|
|
310
|
+
return scrollViewRef.current;
|
|
285
311
|
},
|
|
286
312
|
markChildLayoutAsPending: (id: string) => {
|
|
287
313
|
pendingChildIds.add(id);
|
|
@@ -293,10 +319,13 @@ const RecyclerViewComponent = <T,>(
|
|
|
293
319
|
}
|
|
294
320
|
},
|
|
295
321
|
};
|
|
296
|
-
}, [
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
322
|
+
}, [
|
|
323
|
+
handlerMethods,
|
|
324
|
+
parentRecyclerViewContext,
|
|
325
|
+
pendingChildIds,
|
|
326
|
+
recyclerViewManager.isDisposed,
|
|
327
|
+
setLayoutTreeId,
|
|
328
|
+
]);
|
|
300
329
|
|
|
301
330
|
/**
|
|
302
331
|
* Validates that item dimensions match the expected layout
|
|
@@ -328,7 +357,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
328
357
|
recyclerViewContext.layout();
|
|
329
358
|
}
|
|
330
359
|
},
|
|
331
|
-
[recyclerViewManager]
|
|
360
|
+
[recyclerViewContext, recyclerViewManager]
|
|
332
361
|
);
|
|
333
362
|
|
|
334
363
|
// Get secondary props and components
|
|
@@ -368,7 +397,14 @@ const RecyclerViewComponent = <T,>(
|
|
|
368
397
|
);
|
|
369
398
|
}
|
|
370
399
|
return null;
|
|
371
|
-
}, [
|
|
400
|
+
}, [
|
|
401
|
+
data,
|
|
402
|
+
stickyHeaderIndices,
|
|
403
|
+
renderItem,
|
|
404
|
+
scrollY,
|
|
405
|
+
recyclerViewManager,
|
|
406
|
+
extraData,
|
|
407
|
+
]);
|
|
372
408
|
|
|
373
409
|
// Set up scroll event handling with animation support for sticky headers
|
|
374
410
|
const animatedEvent = useMemo(() => {
|
|
@@ -379,31 +415,24 @@ const RecyclerViewComponent = <T,>(
|
|
|
379
415
|
);
|
|
380
416
|
}
|
|
381
417
|
return onScrollHandler;
|
|
382
|
-
}, [onScrollHandler, stickyHeaders]);
|
|
418
|
+
}, [onScrollHandler, scrollY, stickyHeaders]);
|
|
419
|
+
|
|
420
|
+
const shouldMaintainVisibleContentPosition =
|
|
421
|
+
recyclerViewManager.shouldMaintainVisibleContentPosition();
|
|
383
422
|
|
|
384
423
|
const maintainVisibleContentPositionInternal = useMemo(() => {
|
|
385
|
-
if (
|
|
386
|
-
return undefined;
|
|
387
|
-
} else {
|
|
424
|
+
if (shouldMaintainVisibleContentPosition) {
|
|
388
425
|
return {
|
|
389
426
|
...maintainVisibleContentPosition,
|
|
390
427
|
minIndexForVisible: 0,
|
|
391
428
|
};
|
|
392
429
|
}
|
|
393
|
-
|
|
430
|
+
return undefined;
|
|
431
|
+
}, [maintainVisibleContentPosition, shouldMaintainVisibleContentPosition]);
|
|
394
432
|
|
|
395
433
|
const shouldRenderFromBottom =
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
// Calculate minimum height adjustment for bottom rendering
|
|
399
|
-
const adjustmentMinHeight = recyclerViewManager.hasLayout()
|
|
400
|
-
? Math.max(
|
|
401
|
-
0,
|
|
402
|
-
recyclerViewManager.getWindowSize().height -
|
|
403
|
-
recyclerViewManager.getChildContainerDimensions().height -
|
|
404
|
-
recyclerViewManager.firstItemOffset
|
|
405
|
-
)
|
|
406
|
-
: 0;
|
|
434
|
+
recyclerViewManager.getDataLength() > 0 &&
|
|
435
|
+
(maintainVisibleContentPosition?.startRenderingFromBottom ?? false);
|
|
407
436
|
|
|
408
437
|
// Create view for measuring bounded size
|
|
409
438
|
const viewToMeasureBoundedSize = useMemo(() => {
|
|
@@ -412,12 +441,23 @@ const RecyclerViewComponent = <T,>(
|
|
|
412
441
|
style={{
|
|
413
442
|
height: horizontal ? undefined : 0,
|
|
414
443
|
width: horizontal ? 0 : undefined,
|
|
415
|
-
minHeight: shouldRenderFromBottom ? adjustmentMinHeight : undefined,
|
|
416
444
|
}}
|
|
417
445
|
ref={firstChildViewRef}
|
|
418
446
|
/>
|
|
419
447
|
);
|
|
420
|
-
}, [horizontal
|
|
448
|
+
}, [horizontal]);
|
|
449
|
+
|
|
450
|
+
const scrollAnchor = useMemo(() => {
|
|
451
|
+
if (shouldMaintainVisibleContentPosition) {
|
|
452
|
+
return (
|
|
453
|
+
<ScrollAnchor
|
|
454
|
+
horizontal={Boolean(horizontal)}
|
|
455
|
+
scrollAnchorRef={scrollAnchorRef}
|
|
456
|
+
/>
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
return null;
|
|
460
|
+
}, [horizontal, shouldMaintainVisibleContentPosition]);
|
|
421
461
|
|
|
422
462
|
// console.log("render", recyclerViewManager.getRenderStack());
|
|
423
463
|
|
|
@@ -465,9 +505,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
465
505
|
{...overrideProps}
|
|
466
506
|
>
|
|
467
507
|
{/* Scroll anchor for maintaining content position */}
|
|
468
|
-
{
|
|
469
|
-
<ScrollAnchor scrollAnchorRef={scrollAnchorRef} />
|
|
470
|
-
)}
|
|
508
|
+
{scrollAnchor}
|
|
471
509
|
{isHorizontalRTL && viewToMeasureBoundedSize}
|
|
472
510
|
{renderHeader}
|
|
473
511
|
{!isHorizontalRTL && viewToMeasureBoundedSize}
|
|
@@ -478,6 +516,25 @@ const RecyclerViewComponent = <T,>(
|
|
|
478
516
|
horizontal={horizontal}
|
|
479
517
|
renderStack={recyclerViewManager.getRenderStack()}
|
|
480
518
|
getLayout={(index) => recyclerViewManager.getLayout(index)}
|
|
519
|
+
getAdjustmentMargin={() => {
|
|
520
|
+
if (!shouldRenderFromBottom || !recyclerViewManager.hasLayout()) {
|
|
521
|
+
return 0;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const windowSize = horizontal
|
|
525
|
+
? recyclerViewManager.getWindowSize().width
|
|
526
|
+
: recyclerViewManager.getWindowSize().height;
|
|
527
|
+
const childContainerSize = horizontal
|
|
528
|
+
? recyclerViewManager.getChildContainerDimensions().width
|
|
529
|
+
: recyclerViewManager.getChildContainerDimensions().height;
|
|
530
|
+
|
|
531
|
+
return Math.max(
|
|
532
|
+
0,
|
|
533
|
+
windowSize -
|
|
534
|
+
childContainerSize -
|
|
535
|
+
recyclerViewManager.firstItemOffset
|
|
536
|
+
);
|
|
537
|
+
}}
|
|
481
538
|
refHolder={refHolder}
|
|
482
539
|
onSizeChanged={validateItemSize}
|
|
483
540
|
renderItem={renderItem}
|
|
@@ -490,10 +547,14 @@ const RecyclerViewComponent = <T,>(
|
|
|
490
547
|
onCommitLayoutEffect?.();
|
|
491
548
|
}}
|
|
492
549
|
onCommitEffect={() => {
|
|
550
|
+
renderTimeTracker.markRenderComplete();
|
|
551
|
+
recyclerViewManager.updateAverageRenderTime(
|
|
552
|
+
renderTimeTracker.getAverageRenderTime()
|
|
553
|
+
);
|
|
493
554
|
applyInitialScrollIndex();
|
|
494
555
|
checkBounds();
|
|
495
556
|
recyclerViewManager.computeItemViewability();
|
|
496
|
-
recyclerViewManager.
|
|
557
|
+
recyclerViewManager.animationOptimizationsEnabled = false;
|
|
497
558
|
}}
|
|
498
559
|
CellRendererComponent={CellRendererComponent}
|
|
499
560
|
ItemSeparatorComponent={ItemSeparatorComponent}
|
|
@@ -512,9 +573,12 @@ const RecyclerViewComponent = <T,>(
|
|
|
512
573
|
);
|
|
513
574
|
};
|
|
514
575
|
|
|
576
|
+
// Set displayName for the inner component
|
|
577
|
+
RecyclerViewComponent.displayName = "FlashList";
|
|
578
|
+
|
|
515
579
|
// Type definition for the RecyclerView component
|
|
516
580
|
type RecyclerViewType = <T>(
|
|
517
|
-
props: RecyclerViewProps<T> & { ref?: React.Ref<
|
|
581
|
+
props: RecyclerViewProps<T> & { ref?: React.Ref<FlashListRef<T>> }
|
|
518
582
|
) => React.JSX.Element;
|
|
519
583
|
|
|
520
584
|
// Create and export the memoized, forwarded ref component
|
|
@@ -1,20 +1,65 @@
|
|
|
1
1
|
import { createContext, useContext } from "react";
|
|
2
2
|
|
|
3
|
+
import { FlashListRef } from "../FlashListRef";
|
|
4
|
+
|
|
3
5
|
import { CompatScroller } from "./components/CompatScroller";
|
|
4
6
|
|
|
5
|
-
export interface RecyclerViewContext {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
export interface RecyclerViewContext<T> extends FlashListContext<T> {
|
|
8
|
+
/**
|
|
9
|
+
* Mark the children FlashLists as pending for layout
|
|
10
|
+
* This will force parent FlashList to wait for children to be laid out before
|
|
11
|
+
* rendering the next batch of items.
|
|
12
|
+
* @param id - The id of the child FlashList
|
|
13
|
+
* @returns void
|
|
14
|
+
*/
|
|
9
15
|
markChildLayoutAsPending: (id: string) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Unmark the child FlashList as pending for layout
|
|
18
|
+
* This will allow parent FlashList to render the next batch of items.
|
|
19
|
+
* @param id - The id of the child FlashList
|
|
20
|
+
* @returns void
|
|
21
|
+
*/
|
|
10
22
|
unmarkChildLayoutAsPending: (id: string) => void;
|
|
11
23
|
}
|
|
12
24
|
|
|
25
|
+
export interface FlashListContext<T> {
|
|
26
|
+
/**
|
|
27
|
+
* Layout the current FlashList
|
|
28
|
+
* @returns void
|
|
29
|
+
*/
|
|
30
|
+
layout: () => void;
|
|
31
|
+
/**
|
|
32
|
+
* Get the ref for the current FlashList
|
|
33
|
+
*/
|
|
34
|
+
getRef: () => FlashListRef<T> | null;
|
|
35
|
+
/**
|
|
36
|
+
* Get the ref for the parent FlashList if present
|
|
37
|
+
*/
|
|
38
|
+
getParentRef: () => FlashListRef<unknown> | null;
|
|
39
|
+
/**
|
|
40
|
+
* Get the scrollView ref for the current FlashList if present
|
|
41
|
+
*/
|
|
42
|
+
getScrollViewRef: () => CompatScroller | null;
|
|
43
|
+
/**
|
|
44
|
+
* Get the scrollView ref for the parent FlashList if present
|
|
45
|
+
*/
|
|
46
|
+
getParentScrollViewRef: () => CompatScroller | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
13
49
|
const RecyclerViewContextInstance = createContext<
|
|
14
|
-
RecyclerViewContext | undefined
|
|
50
|
+
RecyclerViewContext<unknown> | undefined
|
|
15
51
|
>(undefined);
|
|
16
52
|
|
|
17
53
|
export const RecyclerViewContextProvider = RecyclerViewContextInstance.Provider;
|
|
18
|
-
export function useRecyclerViewContext()
|
|
19
|
-
|
|
54
|
+
export function useRecyclerViewContext<T>():
|
|
55
|
+
| RecyclerViewContext<T>
|
|
56
|
+
| undefined {
|
|
57
|
+
return useContext(RecyclerViewContextInstance) as
|
|
58
|
+
| RecyclerViewContext<T>
|
|
59
|
+
| undefined;
|
|
60
|
+
}
|
|
61
|
+
export function useFlashListContext<T>(): FlashListContext<T> | undefined {
|
|
62
|
+
return useContext(RecyclerViewContextInstance) as
|
|
63
|
+
| FlashListContext<T>
|
|
64
|
+
| undefined;
|
|
20
65
|
}
|