@shopify/flash-list 2.0.0-alpha.10 → 2.0.0-alpha.12
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 +6 -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 +13 -6
- 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/__tests__/RecyclerView.test.js +62 -27
- 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 +405 -0
- package/dist/__tests__/RenderStackManager.test.js.map +1 -0
- package/dist/__tests__/useUnmountAwareCallbacks.test.js +1 -1
- package/dist/__tests__/useUnmountAwareCallbacks.test.js.map +1 -1
- package/dist/benchmark/useFlatListBenchmark.js +8 -7
- package/dist/benchmark/useFlatListBenchmark.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.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 +39 -21
- package/dist/recyclerview/RecyclerView.js.map +1 -1
- package/dist/recyclerview/RecyclerViewContextProvider.d.ts +6 -5
- package/dist/recyclerview/RecyclerViewContextProvider.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerViewContextProvider.js.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.d.ts +14 -7
- package/dist/recyclerview/RecyclerViewManager.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.js +71 -102
- package/dist/recyclerview/RecyclerViewManager.js.map +1 -1
- package/dist/recyclerview/RenderStackManager.d.ts +85 -0
- package/dist/recyclerview/RenderStackManager.d.ts.map +1 -0
- package/dist/recyclerview/RenderStackManager.js +261 -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 +3 -1
- package/dist/recyclerview/ViewHolderCollection.d.ts.map +1 -1
- package/dist/recyclerview/ViewHolderCollection.js +19 -3
- package/dist/recyclerview/ViewHolderCollection.js.map +1 -1
- package/dist/recyclerview/components/ScrollAnchor.d.ts +2 -1
- package/dist/recyclerview/components/ScrollAnchor.d.ts.map +1 -1
- package/dist/recyclerview/components/ScrollAnchor.js +9 -4
- 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 +39 -32
- package/dist/recyclerview/components/StickyHeaders.js.map +1 -1
- 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 +19 -16
- package/dist/recyclerview/hooks/useBoundDetection.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 +3 -48
- package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.js +111 -77
- package/dist/recyclerview/hooks/useRecyclerViewController.js.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewManager.js +6 -0
- package/dist/recyclerview/hooks/useRecyclerViewManager.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/layout-managers/GridLayoutManager.d.ts +6 -0
- package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/GridLayoutManager.js +27 -5
- package/dist/recyclerview/layout-managers/GridLayoutManager.js.map +1 -1
- package/dist/recyclerview/layout-managers/LayoutManager.d.ts +2 -2
- package/dist/recyclerview/layout-managers/LayoutManager.js +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/jestSetup.js +30 -11
- package/package.json +1 -1
- package/src/AnimatedFlashList.ts +3 -2
- package/src/FlashList.tsx +24 -0
- package/src/FlashListProps.ts +16 -7
- package/src/FlashListRef.ts +320 -0
- package/src/__tests__/RecyclerView.test.tsx +83 -29
- package/src/__tests__/RenderStackManager.test.ts +488 -0
- package/src/__tests__/useUnmountAwareCallbacks.test.tsx +12 -12
- package/src/benchmark/useFlatListBenchmark.ts +2 -2
- package/src/index.ts +1 -0
- package/src/recyclerview/RecyclerView.tsx +49 -29
- package/src/recyclerview/RecyclerViewContextProvider.ts +12 -6
- package/src/recyclerview/RecyclerViewManager.ts +90 -88
- package/src/recyclerview/RenderStackManager.ts +265 -0
- package/src/recyclerview/ViewHolder.tsx +5 -3
- package/src/recyclerview/ViewHolderCollection.tsx +29 -8
- package/src/recyclerview/components/ScrollAnchor.tsx +21 -8
- package/src/recyclerview/components/StickyHeaders.tsx +62 -44
- package/src/recyclerview/hooks/useBoundDetection.ts +25 -18
- package/src/recyclerview/hooks/useOnLoad.ts +4 -6
- package/src/recyclerview/hooks/useRecyclerViewController.tsx +121 -132
- package/src/recyclerview/hooks/useRecyclerViewManager.ts +6 -0
- package/src/recyclerview/hooks/useSecondaryProps.tsx +1 -1
- package/src/recyclerview/hooks/useUnmountAwareCallbacks.ts +39 -3
- package/src/recyclerview/layout-managers/GridLayoutManager.ts +30 -7
- package/src/recyclerview/layout-managers/LayoutManager.ts +2 -2
- 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
|
@@ -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,
|
|
@@ -55,7 +57,7 @@ import { RenderTimeTracker } from "./helpers/RenderTimeTracker";
|
|
|
55
57
|
*/
|
|
56
58
|
const RecyclerViewComponent = <T,>(
|
|
57
59
|
props: RecyclerViewProps<T>,
|
|
58
|
-
ref: React.Ref<
|
|
60
|
+
ref: React.Ref<FlashListRef<T>>
|
|
59
61
|
) => {
|
|
60
62
|
// Destructure props and initialize refs
|
|
61
63
|
const {
|
|
@@ -76,8 +78,6 @@ const RecyclerViewComponent = <T,>(
|
|
|
76
78
|
ListFooterComponentStyle,
|
|
77
79
|
ItemSeparatorComponent,
|
|
78
80
|
renderScrollComponent,
|
|
79
|
-
onScroll,
|
|
80
|
-
disableRecycling,
|
|
81
81
|
style,
|
|
82
82
|
stickyHeaderIndices,
|
|
83
83
|
maintainVisibleContentPosition,
|
|
@@ -116,13 +116,12 @@ const RecyclerViewComponent = <T,>(
|
|
|
116
116
|
// Initialize core RecyclerView manager and content offset management
|
|
117
117
|
const { recyclerViewManager, velocityTracker } =
|
|
118
118
|
useRecyclerViewManager(props);
|
|
119
|
-
const { applyContentOffset, applyInitialScrollIndex } =
|
|
119
|
+
const { applyContentOffset, applyInitialScrollIndex, handlerMethods } =
|
|
120
120
|
useRecyclerViewController(
|
|
121
121
|
recyclerViewManager,
|
|
122
122
|
ref,
|
|
123
123
|
scrollViewRef,
|
|
124
|
-
scrollAnchorRef
|
|
125
|
-
props
|
|
124
|
+
scrollAnchorRef
|
|
126
125
|
);
|
|
127
126
|
|
|
128
127
|
// Initialize view holder collection ref
|
|
@@ -132,11 +131,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
132
131
|
useOnListLoad(recyclerViewManager, onLoad);
|
|
133
132
|
|
|
134
133
|
// Hook to detect when scrolling reaches list bounds
|
|
135
|
-
const { checkBounds } = useBoundDetection(
|
|
136
|
-
recyclerViewManager,
|
|
137
|
-
props,
|
|
138
|
-
scrollViewRef
|
|
139
|
-
);
|
|
134
|
+
const { checkBounds } = useBoundDetection(recyclerViewManager, scrollViewRef);
|
|
140
135
|
|
|
141
136
|
const isHorizontalRTL = I18nManager.isRTL && horizontal;
|
|
142
137
|
|
|
@@ -182,6 +177,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
182
177
|
* Effect to handle layout updates for list items
|
|
183
178
|
* This ensures proper positioning and recycling of items
|
|
184
179
|
*/
|
|
180
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
185
181
|
useLayoutEffect(() => {
|
|
186
182
|
if (pendingChildIds.size > 0) {
|
|
187
183
|
return;
|
|
@@ -189,7 +185,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
189
185
|
const layoutInfo = Array.from(refHolder, ([index, viewHolderRef]) => {
|
|
190
186
|
const layout = measureItemLayout(
|
|
191
187
|
viewHolderRef.current!,
|
|
192
|
-
recyclerViewManager.
|
|
188
|
+
recyclerViewManager.tryGetLayout(index)
|
|
193
189
|
);
|
|
194
190
|
|
|
195
191
|
// comapre height with stored layout
|
|
@@ -272,22 +268,31 @@ const RecyclerViewComponent = <T,>(
|
|
|
272
268
|
recyclerViewManager.computeItemViewability();
|
|
273
269
|
|
|
274
270
|
// Call user-provided onScroll handler
|
|
275
|
-
onScroll?.(event);
|
|
271
|
+
recyclerViewManager.props.onScroll?.(event);
|
|
276
272
|
},
|
|
277
|
-
[
|
|
273
|
+
[
|
|
274
|
+
checkBounds,
|
|
275
|
+
horizontal,
|
|
276
|
+
isHorizontalRTL,
|
|
277
|
+
recyclerViewManager,
|
|
278
|
+
velocityTracker,
|
|
279
|
+
]
|
|
278
280
|
);
|
|
279
281
|
|
|
280
282
|
// Create context for child components
|
|
281
|
-
const recyclerViewContext: RecyclerViewContext = useMemo(() => {
|
|
283
|
+
const recyclerViewContext: RecyclerViewContext<T> = useMemo(() => {
|
|
282
284
|
return {
|
|
283
285
|
layout: () => {
|
|
284
286
|
setLayoutTreeId((prev) => prev + 1);
|
|
285
287
|
},
|
|
286
288
|
getRef: () => {
|
|
287
|
-
|
|
289
|
+
if (recyclerViewManager.isDisposed) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
return handlerMethods;
|
|
288
293
|
},
|
|
289
294
|
getScrollViewRef: () => {
|
|
290
|
-
return scrollViewRef;
|
|
295
|
+
return scrollViewRef.current;
|
|
291
296
|
},
|
|
292
297
|
markChildLayoutAsPending: (id: string) => {
|
|
293
298
|
pendingChildIds.add(id);
|
|
@@ -299,7 +304,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
299
304
|
}
|
|
300
305
|
},
|
|
301
306
|
};
|
|
302
|
-
}, [setLayoutTreeId]);
|
|
307
|
+
}, [handlerMethods, pendingChildIds, recyclerViewManager, setLayoutTreeId]);
|
|
303
308
|
|
|
304
309
|
const parentRecyclerViewContext = useRecyclerViewContext();
|
|
305
310
|
const recyclerViewId = useId();
|
|
@@ -334,7 +339,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
334
339
|
recyclerViewContext.layout();
|
|
335
340
|
}
|
|
336
341
|
},
|
|
337
|
-
[recyclerViewManager]
|
|
342
|
+
[recyclerViewContext, recyclerViewManager]
|
|
338
343
|
);
|
|
339
344
|
|
|
340
345
|
// Get secondary props and components
|
|
@@ -374,7 +379,14 @@ const RecyclerViewComponent = <T,>(
|
|
|
374
379
|
);
|
|
375
380
|
}
|
|
376
381
|
return null;
|
|
377
|
-
}, [
|
|
382
|
+
}, [
|
|
383
|
+
data,
|
|
384
|
+
stickyHeaderIndices,
|
|
385
|
+
renderItem,
|
|
386
|
+
scrollY,
|
|
387
|
+
recyclerViewManager,
|
|
388
|
+
extraData,
|
|
389
|
+
]);
|
|
378
390
|
|
|
379
391
|
// Set up scroll event handling with animation support for sticky headers
|
|
380
392
|
const animatedEvent = useMemo(() => {
|
|
@@ -385,21 +397,23 @@ const RecyclerViewComponent = <T,>(
|
|
|
385
397
|
);
|
|
386
398
|
}
|
|
387
399
|
return onScrollHandler;
|
|
388
|
-
}, [onScrollHandler, stickyHeaders]);
|
|
400
|
+
}, [onScrollHandler, scrollY, stickyHeaders]);
|
|
401
|
+
|
|
402
|
+
const shouldMaintainVisibleContentPosition =
|
|
403
|
+
recyclerViewManager.shouldMaintainVisibleContentPosition();
|
|
389
404
|
|
|
390
405
|
const maintainVisibleContentPositionInternal = useMemo(() => {
|
|
391
|
-
if (
|
|
392
|
-
return undefined;
|
|
393
|
-
} else {
|
|
406
|
+
if (shouldMaintainVisibleContentPosition) {
|
|
394
407
|
return {
|
|
395
408
|
...maintainVisibleContentPosition,
|
|
396
409
|
minIndexForVisible: 0,
|
|
397
410
|
};
|
|
398
411
|
}
|
|
399
|
-
|
|
412
|
+
return undefined;
|
|
413
|
+
}, [maintainVisibleContentPosition, shouldMaintainVisibleContentPosition]);
|
|
400
414
|
|
|
401
415
|
const shouldRenderFromBottom =
|
|
402
|
-
|
|
416
|
+
maintainVisibleContentPosition?.startRenderingFromBottom ?? false;
|
|
403
417
|
|
|
404
418
|
// Calculate minimum height adjustment for bottom rendering
|
|
405
419
|
const adjustmentMinHeight = recyclerViewManager.hasLayout()
|
|
@@ -472,7 +486,10 @@ const RecyclerViewComponent = <T,>(
|
|
|
472
486
|
>
|
|
473
487
|
{/* Scroll anchor for maintaining content position */}
|
|
474
488
|
{maintainVisibleContentPositionInternal && (
|
|
475
|
-
<ScrollAnchor
|
|
489
|
+
<ScrollAnchor
|
|
490
|
+
horizontal={Boolean(horizontal)}
|
|
491
|
+
scrollAnchorRef={scrollAnchorRef}
|
|
492
|
+
/>
|
|
476
493
|
)}
|
|
477
494
|
{isHorizontalRTL && viewToMeasureBoundedSize}
|
|
478
495
|
{renderHeader}
|
|
@@ -503,7 +520,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
503
520
|
applyInitialScrollIndex();
|
|
504
521
|
checkBounds();
|
|
505
522
|
recyclerViewManager.computeItemViewability();
|
|
506
|
-
recyclerViewManager.disableRecycling
|
|
523
|
+
recyclerViewManager.disableRecycling(false);
|
|
507
524
|
}}
|
|
508
525
|
CellRendererComponent={CellRendererComponent}
|
|
509
526
|
ItemSeparatorComponent={ItemSeparatorComponent}
|
|
@@ -522,9 +539,12 @@ const RecyclerViewComponent = <T,>(
|
|
|
522
539
|
);
|
|
523
540
|
};
|
|
524
541
|
|
|
542
|
+
// Set displayName for the inner component
|
|
543
|
+
RecyclerViewComponent.displayName = "FlashList";
|
|
544
|
+
|
|
525
545
|
// Type definition for the RecyclerView component
|
|
526
546
|
type RecyclerViewType = <T>(
|
|
527
|
-
props: RecyclerViewProps<T> & { ref?: React.Ref<
|
|
547
|
+
props: RecyclerViewProps<T> & { ref?: React.Ref<FlashListRef<T>> }
|
|
528
548
|
) => React.JSX.Element;
|
|
529
549
|
|
|
530
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: () =>
|
|
8
|
-
getScrollViewRef: () =>
|
|
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
|
-
|
|
20
|
+
export function useRecyclerViewContext<T>():
|
|
21
|
+
| RecyclerViewContext<T>
|
|
22
|
+
| undefined {
|
|
23
|
+
return useContext(RecyclerViewContextInstance) as
|
|
24
|
+
| RecyclerViewContext<T>
|
|
25
|
+
| undefined;
|
|
20
26
|
}
|
|
@@ -9,30 +9,27 @@ import {
|
|
|
9
9
|
} from "./layout-managers/LayoutManager";
|
|
10
10
|
import { RVLinearLayoutManagerImpl } from "./layout-managers/LinearLayoutManager";
|
|
11
11
|
import { RVMasonryLayoutManagerImpl } from "./layout-managers/MasonryLayoutManager";
|
|
12
|
-
import { RecycleKeyManagerImpl, RecycleKeyManager } from "./RecycleKeyManager";
|
|
13
12
|
import { RecyclerViewProps } from "./RecyclerViewProps";
|
|
14
13
|
import {
|
|
15
14
|
RVEngagedIndicesTracker,
|
|
16
15
|
RVEngagedIndicesTrackerImpl,
|
|
17
16
|
Velocity,
|
|
18
17
|
} from "./helpers/EngagedIndicesTracker";
|
|
19
|
-
|
|
20
|
-
// Abstracts layout manager,
|
|
18
|
+
import { RenderStackManager } from "./RenderStackManager";
|
|
19
|
+
// Abstracts layout manager, render stack manager and viewability manager and generates render stack (progressively on load)
|
|
21
20
|
export class RecyclerViewManager<T> {
|
|
22
21
|
private initialDrawBatchSize = 1;
|
|
23
22
|
private engagedIndicesTracker: RVEngagedIndicesTracker;
|
|
24
|
-
private
|
|
23
|
+
private renderStackManager: RenderStackManager;
|
|
25
24
|
private layoutManager?: RVLayoutManager;
|
|
26
25
|
// Map of index to key
|
|
27
|
-
private renderStack: Map<number, string> = new Map();
|
|
28
26
|
private isFirstLayoutComplete = false;
|
|
29
27
|
private hasRenderedProgressively = false;
|
|
30
|
-
private
|
|
28
|
+
private propsRef: RecyclerViewProps<T>;
|
|
31
29
|
private itemViewabilityManager: ViewabilityManager<T>;
|
|
32
|
-
private allocatedKeyTracker: Set<string> = new Set();
|
|
33
30
|
private _isDisposed = false;
|
|
31
|
+
private _isLayoutManagerDirty = false;
|
|
34
32
|
|
|
35
|
-
public disableRecycling = false;
|
|
36
33
|
public firstItemOffset = 0;
|
|
37
34
|
public ignoreScrollEvents = false;
|
|
38
35
|
|
|
@@ -45,74 +42,46 @@ export class RecyclerViewManager<T> {
|
|
|
45
42
|
}
|
|
46
43
|
|
|
47
44
|
constructor(props: RecyclerViewProps<T>) {
|
|
48
|
-
this.
|
|
45
|
+
this.getStableId = this.getStableId.bind(this);
|
|
46
|
+
this.getItemType = this.getItemType.bind(this);
|
|
47
|
+
this.propsRef = props;
|
|
49
48
|
this.engagedIndicesTracker = new RVEngagedIndicesTrackerImpl();
|
|
50
|
-
this.
|
|
49
|
+
this.renderStackManager = new RenderStackManager(
|
|
50
|
+
props.maxItemsInRecyclePool
|
|
51
|
+
);
|
|
51
52
|
this.itemViewabilityManager = new ViewabilityManager<T>(this as any);
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
// updates render stack based on the engaged indices which are sorted. Recycles unused keys.
|
|
55
|
-
// TODO: Write comprehensive tests for this function
|
|
56
56
|
private updateRenderStack = (engagedIndices: ConsecutiveNumbers): void => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.recycleKeyManager.recycleKey(key);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
if (this.disableRecycling) {
|
|
67
|
-
this.recycleKeyManager.clearPool();
|
|
68
|
-
}
|
|
69
|
-
for (const index of engagedIndices) {
|
|
70
|
-
const currentKey = this.renderStack.get(index);
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
currentKey &&
|
|
74
|
-
!this.disableRecycling &&
|
|
75
|
-
!this.allocatedKeyTracker.has(currentKey)
|
|
76
|
-
) {
|
|
77
|
-
this.recycleKeyManager.recycleKey(currentKey);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const newKey = this.recycleKeyManager.getKey(
|
|
81
|
-
this.getItemType(index),
|
|
82
|
-
this.getStableId(index),
|
|
83
|
-
currentKey
|
|
84
|
-
);
|
|
85
|
-
this.allocatedKeyTracker.add(newKey);
|
|
86
|
-
newRenderStack.set(index, newKey);
|
|
87
|
-
}
|
|
88
|
-
// DANGER
|
|
89
|
-
for (const [index, key] of this.renderStack) {
|
|
90
|
-
if (
|
|
91
|
-
this.recycleKeyManager.hasKeyInPool(key) &&
|
|
92
|
-
!newRenderStack.has(index) &&
|
|
93
|
-
index < (this.props.data?.length ?? 0)
|
|
94
|
-
) {
|
|
95
|
-
this.allocatedKeyTracker.add(key);
|
|
96
|
-
newRenderStack.set(index, key);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
this.renderStack = newRenderStack;
|
|
57
|
+
this.renderStackManager.sync(
|
|
58
|
+
this.getStableId,
|
|
59
|
+
this.getItemType,
|
|
60
|
+
engagedIndices,
|
|
61
|
+
this.getDataLength()
|
|
62
|
+
);
|
|
101
63
|
};
|
|
102
64
|
|
|
65
|
+
get props() {
|
|
66
|
+
return this.propsRef;
|
|
67
|
+
}
|
|
68
|
+
|
|
103
69
|
setOffsetProjectionEnabled(value: boolean) {
|
|
104
70
|
this.engagedIndicesTracker.enableOffsetProjection = value;
|
|
105
71
|
}
|
|
106
72
|
|
|
107
73
|
updateProps(props: RecyclerViewProps<T>) {
|
|
108
|
-
this.
|
|
74
|
+
this.propsRef = props;
|
|
109
75
|
this.engagedIndicesTracker.drawDistance =
|
|
110
76
|
props.drawDistance ?? this.engagedIndicesTracker.drawDistance;
|
|
111
|
-
if (this.
|
|
77
|
+
if (this.propsRef.drawDistance === 0) {
|
|
112
78
|
this.initialDrawBatchSize = 1;
|
|
113
79
|
} else {
|
|
114
80
|
this.initialDrawBatchSize = (props.numColumns ?? 1) * 2;
|
|
115
81
|
}
|
|
82
|
+
this.initialDrawBatchSize =
|
|
83
|
+
this.propsRef.overrideProps?.initialDrawBatchSize ??
|
|
84
|
+
this.initialDrawBatchSize;
|
|
116
85
|
}
|
|
117
86
|
|
|
118
87
|
/**
|
|
@@ -124,7 +93,7 @@ export class RecyclerViewManager<T> {
|
|
|
124
93
|
offset: number,
|
|
125
94
|
velocity?: Velocity
|
|
126
95
|
): ConsecutiveNumbers | undefined {
|
|
127
|
-
if (this.layoutManager && !this.
|
|
96
|
+
if (this.layoutManager && !this._isDisposed) {
|
|
128
97
|
const engagedIndices = this.engagedIndicesTracker.updateScrollOffset(
|
|
129
98
|
offset - this.firstItemOffset,
|
|
130
99
|
velocity,
|
|
@@ -147,6 +116,10 @@ export class RecyclerViewManager<T> {
|
|
|
147
116
|
return this.isFirstLayoutComplete;
|
|
148
117
|
}
|
|
149
118
|
|
|
119
|
+
disableRecycling(disable: boolean) {
|
|
120
|
+
this.renderStackManager.disableRecycling = disable;
|
|
121
|
+
}
|
|
122
|
+
|
|
150
123
|
getLayout(index: number) {
|
|
151
124
|
if (!this.layoutManager) {
|
|
152
125
|
throw new Error(
|
|
@@ -156,6 +129,17 @@ export class RecyclerViewManager<T> {
|
|
|
156
129
|
return this.layoutManager.getLayout(index);
|
|
157
130
|
}
|
|
158
131
|
|
|
132
|
+
tryGetLayout(index: number) {
|
|
133
|
+
if (
|
|
134
|
+
this.layoutManager &&
|
|
135
|
+
index >= 0 &&
|
|
136
|
+
index < this.layoutManager.getLayoutCount()
|
|
137
|
+
) {
|
|
138
|
+
return this.layoutManager.getLayout(index);
|
|
139
|
+
}
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
159
143
|
// Doesn't include header / foot etc
|
|
160
144
|
getChildContainerDimensions() {
|
|
161
145
|
if (!this.layoutManager) {
|
|
@@ -167,7 +151,7 @@ export class RecyclerViewManager<T> {
|
|
|
167
151
|
}
|
|
168
152
|
|
|
169
153
|
getRenderStack() {
|
|
170
|
-
return this.
|
|
154
|
+
return this.renderStackManager.getRenderStack();
|
|
171
155
|
}
|
|
172
156
|
|
|
173
157
|
getWindowSize() {
|
|
@@ -187,10 +171,10 @@ export class RecyclerViewManager<T> {
|
|
|
187
171
|
getMaxScrollOffset() {
|
|
188
172
|
return Math.max(
|
|
189
173
|
0,
|
|
190
|
-
(this.
|
|
174
|
+
(this.propsRef.horizontal
|
|
191
175
|
? this.getChildContainerDimensions().width
|
|
192
176
|
: this.getChildContainerDimensions().height) -
|
|
193
|
-
(this.
|
|
177
|
+
(this.propsRef.horizontal
|
|
194
178
|
? this.getWindowSize().width
|
|
195
179
|
: this.getWindowSize().height) +
|
|
196
180
|
this.firstItemOffset
|
|
@@ -216,28 +200,33 @@ export class RecyclerViewManager<T> {
|
|
|
216
200
|
if (
|
|
217
201
|
this.layoutManager &&
|
|
218
202
|
Boolean(this.layoutManager?.isHorizontal()) !==
|
|
219
|
-
Boolean(this.
|
|
203
|
+
Boolean(this.propsRef.horizontal)
|
|
220
204
|
) {
|
|
221
205
|
throw new Error(
|
|
222
206
|
"Horizontal prop cannot be toggled, you can use a key on FlashList to recreate it."
|
|
223
207
|
);
|
|
224
208
|
}
|
|
209
|
+
if (this._isLayoutManagerDirty) {
|
|
210
|
+
this.layoutManager = undefined;
|
|
211
|
+
this._isLayoutManagerDirty = false;
|
|
212
|
+
}
|
|
225
213
|
if (!(this.layoutManager instanceof LayoutManagerClass)) {
|
|
226
214
|
// console.log("-----> new LayoutManagerClass");
|
|
227
215
|
|
|
228
216
|
this.layoutManager = new LayoutManagerClass(
|
|
229
217
|
{
|
|
230
218
|
windowSize,
|
|
231
|
-
maxColumns: this.
|
|
232
|
-
horizontal: Boolean(this.
|
|
233
|
-
optimizeItemArrangement:
|
|
219
|
+
maxColumns: this.propsRef.numColumns ?? 1,
|
|
220
|
+
horizontal: Boolean(this.propsRef.horizontal),
|
|
221
|
+
optimizeItemArrangement:
|
|
222
|
+
this.propsRef.optimizeItemArrangement ?? true,
|
|
234
223
|
overrideItemLayout: (index, layout) => {
|
|
235
|
-
this.
|
|
224
|
+
this.propsRef?.overrideItemLayout?.(
|
|
236
225
|
layout,
|
|
237
|
-
this.
|
|
226
|
+
this.propsRef.data![index],
|
|
238
227
|
index,
|
|
239
|
-
this.
|
|
240
|
-
this.
|
|
228
|
+
this.propsRef.numColumns ?? 1,
|
|
229
|
+
this.propsRef.extraData
|
|
241
230
|
);
|
|
242
231
|
},
|
|
243
232
|
},
|
|
@@ -246,9 +235,9 @@ export class RecyclerViewManager<T> {
|
|
|
246
235
|
} else {
|
|
247
236
|
this.layoutManager.updateLayoutParams({
|
|
248
237
|
windowSize,
|
|
249
|
-
maxColumns: this.
|
|
250
|
-
horizontal: Boolean(this.
|
|
251
|
-
optimizeItemArrangement: this.
|
|
238
|
+
maxColumns: this.propsRef.numColumns ?? 1,
|
|
239
|
+
horizontal: Boolean(this.propsRef.horizontal),
|
|
240
|
+
optimizeItemArrangement: this.propsRef.optimizeItemArrangement ?? true,
|
|
252
241
|
});
|
|
253
242
|
}
|
|
254
243
|
}
|
|
@@ -257,7 +246,7 @@ export class RecyclerViewManager<T> {
|
|
|
257
246
|
return this.layoutManager !== undefined;
|
|
258
247
|
}
|
|
259
248
|
|
|
260
|
-
|
|
249
|
+
computeVisibleIndices() {
|
|
261
250
|
if (!this.layoutManager) {
|
|
262
251
|
throw new Error(
|
|
263
252
|
"LayoutManager is not initialized, visible indices are not unavailable"
|
|
@@ -295,9 +284,9 @@ export class RecyclerViewManager<T> {
|
|
|
295
284
|
// Using higher buffer for masonry to avoid missing items
|
|
296
285
|
this.itemViewabilityManager.shouldListenToVisibleIndices &&
|
|
297
286
|
this.itemViewabilityManager.updateViewableItems(
|
|
298
|
-
this.
|
|
287
|
+
this.propsRef.masonry
|
|
299
288
|
? this.engagedIndicesTracker.getEngagedIndices().toArray()
|
|
300
|
-
: this.
|
|
289
|
+
: this.computeVisibleIndices().toArray()
|
|
301
290
|
);
|
|
302
291
|
}
|
|
303
292
|
|
|
@@ -311,7 +300,7 @@ export class RecyclerViewManager<T> {
|
|
|
311
300
|
|
|
312
301
|
processDataUpdate() {
|
|
313
302
|
if (this.hasLayout()) {
|
|
314
|
-
this.modifyChildrenLayout([], this.
|
|
303
|
+
this.modifyChildrenLayout([], this.propsRef.data?.length ?? 0);
|
|
315
304
|
if (!this.recomputeEngagedIndices()) {
|
|
316
305
|
// recomputeEngagedIndices will update the render stack if there are any changes in the engaged indices.
|
|
317
306
|
// It's important to update render stack so that elements are assgined right keys incase items were deleted.
|
|
@@ -329,30 +318,42 @@ export class RecyclerViewManager<T> {
|
|
|
329
318
|
this.itemViewabilityManager.dispose();
|
|
330
319
|
}
|
|
331
320
|
|
|
321
|
+
markLayoutManagerDirty() {
|
|
322
|
+
this._isLayoutManagerDirty = true;
|
|
323
|
+
}
|
|
324
|
+
|
|
332
325
|
getInitialScrollIndex() {
|
|
333
326
|
return (
|
|
334
|
-
this.
|
|
335
|
-
(this.
|
|
327
|
+
this.propsRef.initialScrollIndex ??
|
|
328
|
+
(this.propsRef.maintainVisibleContentPosition?.startRenderingFromBottom
|
|
336
329
|
? this.getDataLength() - 1
|
|
337
330
|
: undefined)
|
|
338
331
|
);
|
|
339
332
|
}
|
|
340
333
|
|
|
334
|
+
shouldMaintainVisibleContentPosition() {
|
|
335
|
+
// Return true if maintainVisibleContentPosition is enabled and not horizontal
|
|
336
|
+
return (
|
|
337
|
+
!this.propsRef.maintainVisibleContentPosition?.disabled &&
|
|
338
|
+
!this.propsRef.horizontal
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
341
342
|
getDataLength() {
|
|
342
|
-
return this.
|
|
343
|
+
return this.propsRef.data?.length ?? 0;
|
|
343
344
|
}
|
|
344
345
|
|
|
345
346
|
private getLayoutManagerClass() {
|
|
346
347
|
// throw errors for incompatible props
|
|
347
|
-
if (this.
|
|
348
|
+
if (this.propsRef.masonry && this.propsRef.horizontal) {
|
|
348
349
|
throw new Error("Masonry and horizontal props are incompatible");
|
|
349
350
|
}
|
|
350
|
-
if ((this.
|
|
351
|
+
if ((this.propsRef.numColumns ?? 1) > 1 && this.propsRef.horizontal) {
|
|
351
352
|
throw new Error("numColumns and horizontal props are incompatible");
|
|
352
353
|
}
|
|
353
|
-
return this.
|
|
354
|
+
return this.propsRef.masonry
|
|
354
355
|
? RVMasonryLayoutManagerImpl
|
|
355
|
-
: (this.
|
|
356
|
+
: (this.propsRef.numColumns ?? 1) > 1 && !this.propsRef.horizontal
|
|
356
357
|
? RVGridLayoutManagerImpl
|
|
357
358
|
: RVLinearLayoutManagerImpl;
|
|
358
359
|
}
|
|
@@ -366,7 +367,7 @@ export class RecyclerViewManager<T> {
|
|
|
366
367
|
const initialItemLayout = this.layoutManager?.getLayout(
|
|
367
368
|
initialScrollIndex ?? 0
|
|
368
369
|
);
|
|
369
|
-
const initialItemOffset = this.
|
|
370
|
+
const initialItemOffset = this.propsRef.horizontal
|
|
370
371
|
? initialItemLayout?.x
|
|
371
372
|
: initialItemLayout?.y;
|
|
372
373
|
|
|
@@ -391,7 +392,7 @@ export class RecyclerViewManager<T> {
|
|
|
391
392
|
const layoutManager = this.layoutManager;
|
|
392
393
|
if (layoutManager) {
|
|
393
394
|
this.applyInitialScrollAdjustment();
|
|
394
|
-
const visibleIndices = this.
|
|
395
|
+
const visibleIndices = this.computeVisibleIndices();
|
|
395
396
|
// console.log("---------> visibleIndices", visibleIndices);
|
|
396
397
|
this.hasRenderedProgressively = visibleIndices.every(
|
|
397
398
|
(index) =>
|
|
@@ -412,7 +413,7 @@ export class RecyclerViewManager<T> {
|
|
|
412
413
|
0,
|
|
413
414
|
Math.min(
|
|
414
415
|
visibleIndices.length,
|
|
415
|
-
this.
|
|
416
|
+
this.getRenderStack().size + this.initialDrawBatchSize
|
|
416
417
|
)
|
|
417
418
|
)
|
|
418
419
|
);
|
|
@@ -421,13 +422,14 @@ export class RecyclerViewManager<T> {
|
|
|
421
422
|
|
|
422
423
|
private getItemType(index: number): string {
|
|
423
424
|
return (
|
|
424
|
-
this.
|
|
425
|
+
this.propsRef.getItemType?.(this.propsRef.data![index], index) ??
|
|
426
|
+
"default"
|
|
425
427
|
).toString();
|
|
426
428
|
}
|
|
427
429
|
|
|
428
430
|
private getStableId(index: number): string {
|
|
429
431
|
return (
|
|
430
|
-
this.
|
|
432
|
+
this.propsRef.keyExtractor?.(this.propsRef.data![index], index) ??
|
|
431
433
|
index.toString()
|
|
432
434
|
);
|
|
433
435
|
}
|