@shopify/flash-list 2.0.0-alpha.8 → 2.0.0-rc.1

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.
Files changed (197) hide show
  1. package/README.md +38 -98
  2. package/dist/AnimatedFlashList.d.ts.map +1 -1
  3. package/dist/AnimatedFlashList.js +3 -3
  4. package/dist/AnimatedFlashList.js.map +1 -1
  5. package/dist/FlashList.d.ts +9 -0
  6. package/dist/FlashList.d.ts.map +1 -1
  7. package/dist/FlashList.js +20 -0
  8. package/dist/FlashList.js.map +1 -1
  9. package/dist/FlashListProps.d.ts +15 -8
  10. package/dist/FlashListProps.d.ts.map +1 -1
  11. package/dist/FlashListProps.js.map +1 -1
  12. package/dist/FlashListRef.d.ts +305 -0
  13. package/dist/FlashListRef.d.ts.map +1 -0
  14. package/dist/FlashListRef.js +3 -0
  15. package/dist/FlashListRef.js.map +1 -0
  16. package/dist/MasonryFlashList.js.map +1 -1
  17. package/dist/__tests__/RecyclerView.test.js +63 -28
  18. package/dist/__tests__/RecyclerView.test.js.map +1 -1
  19. package/dist/__tests__/RenderStackManager.test.d.ts +2 -0
  20. package/dist/__tests__/RenderStackManager.test.d.ts.map +1 -0
  21. package/dist/__tests__/RenderStackManager.test.js +486 -0
  22. package/dist/__tests__/RenderStackManager.test.js.map +1 -0
  23. package/dist/__tests__/helpers/createLayoutManager.d.ts.map +1 -1
  24. package/dist/__tests__/helpers/createLayoutManager.js +3 -4
  25. package/dist/__tests__/helpers/createLayoutManager.js.map +1 -1
  26. package/dist/__tests__/useUnmountAwareCallbacks.test.js +1 -1
  27. package/dist/__tests__/useUnmountAwareCallbacks.test.js.map +1 -1
  28. package/dist/benchmark/useFlatListBenchmark.js +8 -7
  29. package/dist/benchmark/useFlatListBenchmark.js.map +1 -1
  30. package/dist/enableNewCore.d.ts.map +1 -1
  31. package/dist/enableNewCore.js +2 -1
  32. package/dist/enableNewCore.js.map +1 -1
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/native/config/PlatformHelper.android.d.ts +2 -0
  37. package/dist/native/config/PlatformHelper.android.d.ts.map +1 -1
  38. package/dist/native/config/PlatformHelper.android.js +2 -0
  39. package/dist/native/config/PlatformHelper.android.js.map +1 -1
  40. package/dist/native/config/PlatformHelper.d.ts +2 -0
  41. package/dist/native/config/PlatformHelper.d.ts.map +1 -1
  42. package/dist/native/config/PlatformHelper.ios.d.ts +2 -0
  43. package/dist/native/config/PlatformHelper.ios.d.ts.map +1 -1
  44. package/dist/native/config/PlatformHelper.ios.js +2 -0
  45. package/dist/native/config/PlatformHelper.ios.js.map +1 -1
  46. package/dist/native/config/PlatformHelper.js +2 -0
  47. package/dist/native/config/PlatformHelper.js.map +1 -1
  48. package/dist/native/config/PlatformHelper.web.d.ts +2 -0
  49. package/dist/native/config/PlatformHelper.web.d.ts.map +1 -1
  50. package/dist/native/config/PlatformHelper.web.js +3 -1
  51. package/dist/native/config/PlatformHelper.web.js.map +1 -1
  52. package/dist/recyclerview/RecyclerView.d.ts +2 -1
  53. package/dist/recyclerview/RecyclerView.d.ts.map +1 -1
  54. package/dist/recyclerview/RecyclerView.js +64 -37
  55. package/dist/recyclerview/RecyclerView.js.map +1 -1
  56. package/dist/recyclerview/RecyclerViewContextProvider.d.ts +6 -5
  57. package/dist/recyclerview/RecyclerViewContextProvider.d.ts.map +1 -1
  58. package/dist/recyclerview/RecyclerViewContextProvider.js.map +1 -1
  59. package/dist/recyclerview/RecyclerViewManager.d.ts +21 -7
  60. package/dist/recyclerview/RecyclerViewManager.d.ts.map +1 -1
  61. package/dist/recyclerview/RecyclerViewManager.js +105 -113
  62. package/dist/recyclerview/RecyclerViewManager.js.map +1 -1
  63. package/dist/recyclerview/RenderStackManager.d.ts +85 -0
  64. package/dist/recyclerview/RenderStackManager.d.ts.map +1 -0
  65. package/dist/recyclerview/RenderStackManager.js +324 -0
  66. package/dist/recyclerview/RenderStackManager.js.map +1 -0
  67. package/dist/recyclerview/ViewHolder.d.ts.map +1 -1
  68. package/dist/recyclerview/ViewHolder.js +5 -3
  69. package/dist/recyclerview/ViewHolder.js.map +1 -1
  70. package/dist/recyclerview/ViewHolderCollection.d.ts +3 -1
  71. package/dist/recyclerview/ViewHolderCollection.d.ts.map +1 -1
  72. package/dist/recyclerview/ViewHolderCollection.js +23 -8
  73. package/dist/recyclerview/ViewHolderCollection.js.map +1 -1
  74. package/dist/recyclerview/components/ScrollAnchor.d.ts +2 -1
  75. package/dist/recyclerview/components/ScrollAnchor.d.ts.map +1 -1
  76. package/dist/recyclerview/components/ScrollAnchor.js +9 -4
  77. package/dist/recyclerview/components/ScrollAnchor.js.map +1 -1
  78. package/dist/recyclerview/components/StickyHeaders.d.ts +1 -1
  79. package/dist/recyclerview/components/StickyHeaders.d.ts.map +1 -1
  80. package/dist/recyclerview/components/StickyHeaders.js +40 -32
  81. package/dist/recyclerview/components/StickyHeaders.js.map +1 -1
  82. package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts +45 -1
  83. package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts.map +1 -1
  84. package/dist/recyclerview/helpers/EngagedIndicesTracker.js +79 -20
  85. package/dist/recyclerview/helpers/EngagedIndicesTracker.js.map +1 -1
  86. package/dist/recyclerview/helpers/RenderTimeTracker.d.ts +10 -0
  87. package/dist/recyclerview/helpers/RenderTimeTracker.d.ts.map +1 -0
  88. package/dist/recyclerview/helpers/RenderTimeTracker.js +39 -0
  89. package/dist/recyclerview/helpers/RenderTimeTracker.js.map +1 -0
  90. package/dist/recyclerview/helpers/VelocityTracker.d.ts +29 -0
  91. package/dist/recyclerview/helpers/VelocityTracker.d.ts.map +1 -0
  92. package/dist/recyclerview/helpers/VelocityTracker.js +70 -0
  93. package/dist/recyclerview/helpers/VelocityTracker.js.map +1 -0
  94. package/dist/recyclerview/hooks/useBoundDetection.d.ts +1 -2
  95. package/dist/recyclerview/hooks/useBoundDetection.d.ts.map +1 -1
  96. package/dist/recyclerview/hooks/useBoundDetection.js +19 -16
  97. package/dist/recyclerview/hooks/useBoundDetection.js.map +1 -1
  98. package/dist/recyclerview/hooks/useMappingHelper.d.ts +1 -1
  99. package/dist/recyclerview/hooks/useMappingHelper.d.ts.map +1 -1
  100. package/dist/recyclerview/hooks/useMappingHelper.js +1 -1
  101. package/dist/recyclerview/hooks/useMappingHelper.js.map +1 -1
  102. package/dist/recyclerview/hooks/useOnLoad.d.ts.map +1 -1
  103. package/dist/recyclerview/hooks/useOnLoad.js +4 -6
  104. package/dist/recyclerview/hooks/useOnLoad.js.map +1 -1
  105. package/dist/recyclerview/hooks/useRecyclerViewController.d.ts +3 -48
  106. package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -1
  107. package/dist/recyclerview/hooks/useRecyclerViewController.js +179 -119
  108. package/dist/recyclerview/hooks/useRecyclerViewController.js.map +1 -1
  109. package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts +2 -0
  110. package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts.map +1 -1
  111. package/dist/recyclerview/hooks/useRecyclerViewManager.js +10 -1
  112. package/dist/recyclerview/hooks/useRecyclerViewManager.js.map +1 -1
  113. package/dist/recyclerview/hooks/useSecondaryProps.js +1 -1
  114. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.d.ts +10 -3
  115. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.d.ts.map +1 -1
  116. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js +33 -4
  117. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js.map +1 -1
  118. package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts +6 -0
  119. package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts.map +1 -1
  120. package/dist/recyclerview/layout-managers/GridLayoutManager.js +27 -5
  121. package/dist/recyclerview/layout-managers/GridLayoutManager.js.map +1 -1
  122. package/dist/recyclerview/layout-managers/LayoutManager.d.ts +10 -16
  123. package/dist/recyclerview/layout-managers/LayoutManager.d.ts.map +1 -1
  124. package/dist/recyclerview/layout-managers/LayoutManager.js +4 -14
  125. package/dist/recyclerview/layout-managers/LayoutManager.js.map +1 -1
  126. package/dist/recyclerview/layout-managers/MasonryLayoutManager.js +2 -2
  127. package/dist/recyclerview/layout-managers/MasonryLayoutManager.js.map +1 -1
  128. package/dist/recyclerview/utils/measureLayout.d.ts +1 -29
  129. package/dist/recyclerview/utils/measureLayout.d.ts.map +1 -1
  130. package/dist/recyclerview/utils/measureLayout.js +2 -4
  131. package/dist/recyclerview/utils/measureLayout.js.map +1 -1
  132. package/dist/recyclerview/utils/measureLayout.web.d.ts +29 -0
  133. package/dist/recyclerview/utils/measureLayout.web.d.ts.map +1 -0
  134. package/dist/recyclerview/utils/measureLayout.web.js +89 -0
  135. package/dist/recyclerview/utils/measureLayout.web.js.map +1 -0
  136. package/dist/tsconfig.tsbuildinfo +1 -1
  137. package/dist/viewability/ViewToken.d.ts +2 -2
  138. package/dist/viewability/ViewToken.d.ts.map +1 -1
  139. package/dist/viewability/ViewabilityHelper.js +1 -1
  140. package/dist/viewability/ViewabilityHelper.js.map +1 -1
  141. package/dist/viewability/ViewabilityManager.d.ts.map +1 -1
  142. package/dist/viewability/ViewabilityManager.js +1 -2
  143. package/dist/viewability/ViewabilityManager.js.map +1 -1
  144. package/jestSetup.js +30 -11
  145. package/package.json +2 -1
  146. package/src/AnimatedFlashList.ts +3 -2
  147. package/src/FlashList.tsx +24 -0
  148. package/src/FlashListProps.ts +20 -8
  149. package/src/FlashListRef.ts +320 -0
  150. package/src/MasonryFlashList.tsx +2 -2
  151. package/src/__tests__/RecyclerView.test.tsx +84 -30
  152. package/src/__tests__/RenderStackManager.test.ts +575 -0
  153. package/src/__tests__/helpers/createLayoutManager.ts +2 -3
  154. package/src/__tests__/useUnmountAwareCallbacks.test.tsx +12 -12
  155. package/src/benchmark/useFlatListBenchmark.ts +2 -2
  156. package/src/enableNewCore.ts +3 -1
  157. package/src/index.ts +1 -0
  158. package/src/native/config/PlatformHelper.android.ts +2 -0
  159. package/src/native/config/PlatformHelper.ios.ts +2 -0
  160. package/src/native/config/PlatformHelper.ts +2 -0
  161. package/src/native/config/PlatformHelper.web.ts +3 -1
  162. package/src/recyclerview/RecyclerView.tsx +85 -43
  163. package/src/recyclerview/RecyclerViewContextProvider.ts +12 -6
  164. package/src/recyclerview/RecyclerViewManager.ts +123 -98
  165. package/src/recyclerview/RenderStackManager.ts +291 -0
  166. package/src/recyclerview/ViewHolder.tsx +5 -3
  167. package/src/recyclerview/ViewHolderCollection.tsx +33 -12
  168. package/src/recyclerview/components/ScrollAnchor.tsx +21 -8
  169. package/src/recyclerview/components/StickyHeaders.tsx +63 -44
  170. package/src/recyclerview/helpers/EngagedIndicesTracker.ts +120 -23
  171. package/src/recyclerview/helpers/RenderTimeTracker.ts +38 -0
  172. package/src/recyclerview/helpers/VelocityTracker.ts +77 -0
  173. package/src/recyclerview/hooks/useBoundDetection.ts +25 -18
  174. package/src/recyclerview/hooks/useMappingHelper.ts +1 -1
  175. package/src/recyclerview/hooks/useOnLoad.ts +4 -6
  176. package/src/recyclerview/hooks/useRecyclerViewController.tsx +204 -173
  177. package/src/recyclerview/hooks/useRecyclerViewManager.ts +11 -1
  178. package/src/recyclerview/hooks/useSecondaryProps.tsx +1 -1
  179. package/src/recyclerview/hooks/useUnmountAwareCallbacks.ts +39 -3
  180. package/src/recyclerview/layout-managers/GridLayoutManager.ts +30 -7
  181. package/src/recyclerview/layout-managers/LayoutManager.ts +12 -21
  182. package/src/recyclerview/layout-managers/MasonryLayoutManager.ts +1 -1
  183. package/src/recyclerview/utils/measureLayout.ts +3 -3
  184. package/src/recyclerview/utils/measureLayout.web.ts +104 -0
  185. package/src/viewability/ViewToken.ts +2 -2
  186. package/src/viewability/ViewabilityHelper.ts +1 -1
  187. package/src/viewability/ViewabilityManager.ts +6 -3
  188. package/dist/__tests__/RecycleKeyManager.test.d.ts +0 -2
  189. package/dist/__tests__/RecycleKeyManager.test.d.ts.map +0 -1
  190. package/dist/__tests__/RecycleKeyManager.test.js +0 -210
  191. package/dist/__tests__/RecycleKeyManager.test.js.map +0 -1
  192. package/dist/recyclerview/RecycleKeyManager.d.ts +0 -82
  193. package/dist/recyclerview/RecycleKeyManager.d.ts.map +0 -1
  194. package/dist/recyclerview/RecycleKeyManager.js +0 -135
  195. package/dist/recyclerview/RecycleKeyManager.js.map +0 -1
  196. package/src/__tests__/RecycleKeyManager.test.ts +0 -254
  197. package/src/recyclerview/RecycleKeyManager.ts +0 -185
@@ -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.horizontal;
74
+ const horizontal = Boolean(flatListRef.current.props?.horizontal);
75
75
 
76
76
  const fromX = 0;
77
77
  const fromY = 0;
@@ -1,3 +1,5 @@
1
+ import { Platform } from "react-native";
2
+
1
3
  let useNewCore: boolean | undefined;
2
4
  export function enableNewCore(enable: boolean) {
3
5
  useNewCore = enable;
@@ -15,7 +17,7 @@ function isReactNativeNewArchitecture(): boolean {
15
17
  // Check for TurboModule system
16
18
  const hasTurboModule = Boolean((global as any)?.__turboModuleProxy);
17
19
 
18
- return hasFabricUIManager || hasTurboModule;
20
+ return hasFabricUIManager || hasTurboModule || Platform.OS === "web";
19
21
  } catch {
20
22
  return false;
21
23
  }
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,
@@ -2,6 +2,8 @@ import { BaseItemAnimator } from "recyclerlistview";
2
2
 
3
3
  const PlatformConfig = {
4
4
  defaultDrawDistance: 250,
5
+ supportsOffsetCorrection: true,
6
+ trackAverageRenderTimeForOffsetProjection: true,
5
7
  // Using rotate instead of scaleY on Android to avoid performance issues. Issue: https://github.com/Shopify/flash-list/issues/751
6
8
  invertedTransformStyle: { transform: [{ rotate: "180deg" }] },
7
9
  invertedTransformStyleHorizontal: { transform: [{ rotate: "180deg" }] },
@@ -2,6 +2,8 @@ import { BaseItemAnimator } from "recyclerlistview";
2
2
 
3
3
  const PlatformConfig = {
4
4
  defaultDrawDistance: 250,
5
+ supportsOffsetCorrection: true,
6
+ trackAverageRenderTimeForOffsetProjection: false,
5
7
  invertedTransformStyle: { transform: [{ scaleY: -1 }] },
6
8
  invertedTransformStyleHorizontal: { transform: [{ scaleX: -1 }] },
7
9
  };
@@ -3,6 +3,8 @@ import { DefaultJSItemAnimator } from "recyclerlistview/dist/reactnative/platfor
3
3
 
4
4
  const PlatformConfig = {
5
5
  defaultDrawDistance: 250,
6
+ supportsOffsetCorrection: false,
7
+ trackAverageRenderTimeForOffsetProjection: false,
6
8
  invertedTransformStyle: { transform: [{ scaleY: -1 }] },
7
9
  invertedTransformStyleHorizontal: { transform: [{ scaleX: -1 }] },
8
10
  };
@@ -4,7 +4,9 @@ import { BaseItemAnimator } from "recyclerlistview";
4
4
  import { DefaultJSItemAnimator } from "recyclerlistview/dist/reactnative/platform/reactnative/itemanimators/defaultjsanimator/DefaultJSItemAnimator";
5
5
 
6
6
  const PlatformConfig = {
7
- defaultDrawDistance: 2000,
7
+ defaultDrawDistance: 500,
8
+ supportsOffsetCorrection: false,
9
+ trackAverageRenderTimeForOffsetProjection: false,
8
10
  invertedTransformStyle: { transform: [{ scaleY: -1 }] },
9
11
  invertedTransformStyleHorizontal: { transform: [{ scaleX: -1 }] },
10
12
  };
@@ -19,10 +19,12 @@ 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,
25
- measureChildContainerLayout,
27
+ measureFirstChildLayout,
26
28
  measureItemLayout,
27
29
  measureParentSize,
28
30
  } from "./utils/measureLayout";
@@ -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<any>
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,14 +114,14 @@ const RecyclerViewComponent = <T,>(
109
114
  );
110
115
 
111
116
  // Initialize core RecyclerView manager and content offset management
112
- const { recyclerViewManager } = useRecyclerViewManager(props);
113
- const { applyContentOffset, applyInitialScrollIndex } =
117
+ const { recyclerViewManager, velocityTracker } =
118
+ useRecyclerViewManager(props);
119
+ const { applyContentOffset, applyInitialScrollIndex, handlerMethods } =
114
120
  useRecyclerViewController(
115
121
  recyclerViewManager,
116
122
  ref,
117
123
  scrollViewRef,
118
- scrollAnchorRef,
119
- props
124
+ scrollAnchorRef
120
125
  );
121
126
 
122
127
  // Initialize view holder collection ref
@@ -126,11 +131,7 @@ const RecyclerViewComponent = <T,>(
126
131
  useOnListLoad(recyclerViewManager, onLoad);
127
132
 
128
133
  // Hook to detect when scrolling reaches list bounds
129
- const { checkBounds } = useBoundDetection(
130
- recyclerViewManager,
131
- props,
132
- scrollViewRef
133
- );
134
+ const { checkBounds } = useBoundDetection(recyclerViewManager, scrollViewRef);
134
135
 
135
136
  const isHorizontalRTL = I18nManager.isRTL && horizontal;
136
137
 
@@ -142,7 +143,7 @@ const RecyclerViewComponent = <T,>(
142
143
  if (internalViewRef.current && firstChildViewRef.current) {
143
144
  // Measure the outer and inner container layouts
144
145
  const outerViewLayout = measureParentSize(internalViewRef.current);
145
- const firstChildViewLayout = measureChildContainerLayout(
146
+ const firstChildViewLayout = measureFirstChildLayout(
146
147
  firstChildViewRef.current,
147
148
  internalViewRef.current
148
149
  );
@@ -176,6 +177,7 @@ const RecyclerViewComponent = <T,>(
176
177
  * Effect to handle layout updates for list items
177
178
  * This ensures proper positioning and recycling of items
178
179
  */
180
+ // eslint-disable-next-line react-hooks/exhaustive-deps
179
181
  useLayoutEffect(() => {
180
182
  if (pendingChildIds.size > 0) {
181
183
  return;
@@ -183,7 +185,7 @@ const RecyclerViewComponent = <T,>(
183
185
  const layoutInfo = Array.from(refHolder, ([index, viewHolderRef]) => {
184
186
  const layout = measureItemLayout(
185
187
  viewHolderRef.current!,
186
- recyclerViewManager.getLayout(index)
188
+ recyclerViewManager.tryGetLayout(index)
187
189
  );
188
190
 
189
191
  // comapre height with stored layout
@@ -221,7 +223,7 @@ const RecyclerViewComponent = <T,>(
221
223
  if (recyclerViewManager.ignoreScrollEvents) {
222
224
  return;
223
225
  }
224
- let velocity = event.nativeEvent.velocity;
226
+
225
227
  let scrollOffset = horizontal
226
228
  ? event.nativeEvent.contentOffset.x
227
229
  : event.nativeEvent.contentOffset.y;
@@ -233,18 +235,30 @@ const RecyclerViewComponent = <T,>(
233
235
  event.nativeEvent.contentSize.width,
234
236
  event.nativeEvent.layoutMeasurement.width
235
237
  );
236
- if (velocity) {
237
- velocity = {
238
- x: -velocity.x,
239
- y: velocity.y,
240
- };
241
- }
242
- }
243
- // Update scroll position and trigger re-render if needed
244
- if (recyclerViewManager.updateScrollOffset(scrollOffset, velocity)) {
245
- setRenderId((prev) => prev + 1);
246
238
  }
247
239
 
240
+ velocityTracker.computeVelocity(
241
+ scrollOffset,
242
+ recyclerViewManager.getAbsoluteLastScrollOffset(),
243
+ Boolean(horizontal),
244
+ (velocity, isMomentumEnd) => {
245
+ if (recyclerViewManager.ignoreScrollEvents) {
246
+ return;
247
+ }
248
+
249
+ if (isMomentumEnd) {
250
+ if (!recyclerViewManager.isOffsetProjectionEnabled) {
251
+ return;
252
+ }
253
+ recyclerViewManager.resetVelocityCompute();
254
+ }
255
+ // Update scroll position and trigger re-render if needed
256
+ if (recyclerViewManager.updateScrollOffset(scrollOffset, velocity)) {
257
+ setRenderId((prev) => prev + 1);
258
+ }
259
+ }
260
+ );
261
+
248
262
  // Update sticky headers and check bounds
249
263
  stickyHeaderRef.current?.reportScrollEvent(event.nativeEvent);
250
264
  checkBounds();
@@ -254,22 +268,31 @@ const RecyclerViewComponent = <T,>(
254
268
  recyclerViewManager.computeItemViewability();
255
269
 
256
270
  // Call user-provided onScroll handler
257
- onScroll?.(event);
271
+ recyclerViewManager.props.onScroll?.(event);
258
272
  },
259
- [horizontal, isHorizontalRTL, recyclerViewManager]
273
+ [
274
+ checkBounds,
275
+ horizontal,
276
+ isHorizontalRTL,
277
+ recyclerViewManager,
278
+ velocityTracker,
279
+ ]
260
280
  );
261
281
 
262
282
  // Create context for child components
263
- const recyclerViewContext: RecyclerViewContext = useMemo(() => {
283
+ const recyclerViewContext: RecyclerViewContext<T> = useMemo(() => {
264
284
  return {
265
285
  layout: () => {
266
286
  setLayoutTreeId((prev) => prev + 1);
267
287
  },
268
288
  getRef: () => {
269
- return ref;
289
+ if (recyclerViewManager.isDisposed) {
290
+ return null;
291
+ }
292
+ return handlerMethods;
270
293
  },
271
294
  getScrollViewRef: () => {
272
- return scrollViewRef;
295
+ return scrollViewRef.current;
273
296
  },
274
297
  markChildLayoutAsPending: (id: string) => {
275
298
  pendingChildIds.add(id);
@@ -281,7 +304,7 @@ const RecyclerViewComponent = <T,>(
281
304
  }
282
305
  },
283
306
  };
284
- }, [setLayoutTreeId]);
307
+ }, [handlerMethods, pendingChildIds, recyclerViewManager, setLayoutTreeId]);
285
308
 
286
309
  const parentRecyclerViewContext = useRecyclerViewContext();
287
310
  const recyclerViewId = useId();
@@ -316,7 +339,7 @@ const RecyclerViewComponent = <T,>(
316
339
  recyclerViewContext.layout();
317
340
  }
318
341
  },
319
- [recyclerViewManager]
342
+ [recyclerViewContext, recyclerViewManager]
320
343
  );
321
344
 
322
345
  // Get secondary props and components
@@ -356,7 +379,14 @@ const RecyclerViewComponent = <T,>(
356
379
  );
357
380
  }
358
381
  return null;
359
- }, [data, stickyHeaderIndices, renderItem, extraData]);
382
+ }, [
383
+ data,
384
+ stickyHeaderIndices,
385
+ renderItem,
386
+ scrollY,
387
+ recyclerViewManager,
388
+ extraData,
389
+ ]);
360
390
 
361
391
  // Set up scroll event handling with animation support for sticky headers
362
392
  const animatedEvent = useMemo(() => {
@@ -367,21 +397,23 @@ const RecyclerViewComponent = <T,>(
367
397
  );
368
398
  }
369
399
  return onScrollHandler;
370
- }, [onScrollHandler, stickyHeaders]);
400
+ }, [onScrollHandler, scrollY, stickyHeaders]);
401
+
402
+ const shouldMaintainVisibleContentPosition =
403
+ recyclerViewManager.shouldMaintainVisibleContentPosition();
371
404
 
372
405
  const maintainVisibleContentPositionInternal = useMemo(() => {
373
- if (maintainVisibleContentPosition?.disabled || horizontal) {
374
- return undefined;
375
- } else {
406
+ if (shouldMaintainVisibleContentPosition) {
376
407
  return {
377
408
  ...maintainVisibleContentPosition,
378
409
  minIndexForVisible: 0,
379
410
  };
380
411
  }
381
- }, [maintainVisibleContentPosition]);
412
+ return undefined;
413
+ }, [maintainVisibleContentPosition, shouldMaintainVisibleContentPosition]);
382
414
 
383
415
  const shouldRenderFromBottom =
384
- maintainVisibleContentPositionInternal?.startRenderingFromBottom ?? false;
416
+ maintainVisibleContentPosition?.startRenderingFromBottom ?? false;
385
417
 
386
418
  // Calculate minimum height adjustment for bottom rendering
387
419
  const adjustmentMinHeight = recyclerViewManager.hasLayout()
@@ -454,7 +486,10 @@ const RecyclerViewComponent = <T,>(
454
486
  >
455
487
  {/* Scroll anchor for maintaining content position */}
456
488
  {maintainVisibleContentPositionInternal && (
457
- <ScrollAnchor scrollAnchorRef={scrollAnchorRef} />
489
+ <ScrollAnchor
490
+ horizontal={Boolean(horizontal)}
491
+ scrollAnchorRef={scrollAnchorRef}
492
+ />
458
493
  )}
459
494
  {isHorizontalRTL && viewToMeasureBoundedSize}
460
495
  {renderHeader}
@@ -478,10 +513,14 @@ const RecyclerViewComponent = <T,>(
478
513
  onCommitLayoutEffect?.();
479
514
  }}
480
515
  onCommitEffect={() => {
516
+ renderTimeTracker.markRenderComplete();
517
+ recyclerViewManager.updateAverageRenderTime(
518
+ renderTimeTracker.getAverageRenderTime()
519
+ );
481
520
  applyInitialScrollIndex();
482
521
  checkBounds();
483
522
  recyclerViewManager.computeItemViewability();
484
- recyclerViewManager.disableRecycling = Boolean(disableRecycling);
523
+ recyclerViewManager.disableRecycling(false);
485
524
  }}
486
525
  CellRendererComponent={CellRendererComponent}
487
526
  ItemSeparatorComponent={ItemSeparatorComponent}
@@ -500,9 +539,12 @@ const RecyclerViewComponent = <T,>(
500
539
  );
501
540
  };
502
541
 
542
+ // Set displayName for the inner component
543
+ RecyclerViewComponent.displayName = "FlashList";
544
+
503
545
  // Type definition for the RecyclerView component
504
546
  type RecyclerViewType = <T>(
505
- props: RecyclerViewProps<T> & { ref?: React.Ref<any> }
547
+ props: RecyclerViewProps<T> & { ref?: React.Ref<FlashListRef<T>> }
506
548
  ) => React.JSX.Element;
507
549
 
508
550
  // Create and export the memoized, forwarded ref component
@@ -1,20 +1,26 @@
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 {
7
+ export interface RecyclerViewContext<T> {
6
8
  layout: () => void;
7
- getRef: () => React.Ref<any>;
8
- getScrollViewRef: () => React.RefObject<CompatScroller | null>;
9
+ getRef: () => FlashListRef<T> | null;
10
+ getScrollViewRef: () => CompatScroller | null;
9
11
  markChildLayoutAsPending: (id: string) => void;
10
12
  unmarkChildLayoutAsPending: (id: string) => void;
11
13
  }
12
14
 
13
15
  const RecyclerViewContextInstance = createContext<
14
- RecyclerViewContext | undefined
16
+ RecyclerViewContext<unknown> | undefined
15
17
  >(undefined);
16
18
 
17
19
  export const RecyclerViewContextProvider = RecyclerViewContextInstance.Provider;
18
- export function useRecyclerViewContext() {
19
- return useContext(RecyclerViewContextInstance);
20
+ export function useRecyclerViewContext<T>():
21
+ | RecyclerViewContext<T>
22
+ | undefined {
23
+ return useContext(RecyclerViewContextInstance) as
24
+ | RecyclerViewContext<T>
25
+ | undefined;
20
26
  }