@shopify/flash-list 2.0.0-rc.2 → 2.0.0-rc.4
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/dist/FlashListProps.d.ts +10 -2
- package/dist/FlashListProps.d.ts.map +1 -1
- package/dist/FlashListProps.js.map +1 -1
- package/dist/__tests__/RenderStackManager.test.js +1 -2
- package/dist/__tests__/RenderStackManager.test.js.map +1 -1
- package/dist/recyclerview/RecyclerView.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerView.js +11 -3
- package/dist/recyclerview/RecyclerView.js.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.d.ts +1 -0
- package/dist/recyclerview/RecyclerViewManager.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.js +5 -0
- package/dist/recyclerview/RecyclerViewManager.js.map +1 -1
- package/dist/recyclerview/RenderStackManager.d.ts +1 -0
- package/dist/recyclerview/RenderStackManager.d.ts.map +1 -1
- package/dist/recyclerview/RenderStackManager.js +26 -7
- package/dist/recyclerview/RenderStackManager.js.map +1 -1
- package/dist/recyclerview/helpers/RenderTimeTracker.d.ts +1 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.d.ts.map +1 -1
- package/dist/recyclerview/helpers/RenderTimeTracker.js +3 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.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/useRecyclerViewController.d.ts +2 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.js +237 -190
- 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 +2 -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/layout-managers/GridLayoutManager.d.ts +14 -6
- package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/GridLayoutManager.js +40 -23
- package/dist/recyclerview/layout-managers/GridLayoutManager.js.map +1 -1
- package/dist/recyclerview/layout-managers/LayoutManager.d.ts +26 -6
- package/dist/recyclerview/layout-managers/LayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/LayoutManager.js +89 -15
- 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/tsconfig.tsbuildinfo +1 -1
- package/dist/viewability/ViewabilityManager.d.ts.map +1 -1
- package/dist/viewability/ViewabilityManager.js +10 -3
- package/dist/viewability/ViewabilityManager.js.map +1 -1
- package/package.json +1 -1
- package/src/FlashListProps.ts +16 -2
- package/src/__tests__/RenderStackManager.test.ts +1 -2
- package/src/recyclerview/RecyclerView.tsx +27 -14
- package/src/recyclerview/RecyclerViewManager.ts +6 -0
- package/src/recyclerview/RenderStackManager.ts +32 -6
- package/src/recyclerview/helpers/RenderTimeTracker.ts +4 -0
- package/src/recyclerview/hooks/useLayoutState.ts +15 -6
- package/src/recyclerview/hooks/useRecyclerViewController.tsx +240 -168
- package/src/recyclerview/hooks/useRecyclerViewManager.ts +3 -1
- package/src/recyclerview/hooks/useRecyclingState.ts +11 -7
- package/src/recyclerview/layout-managers/GridLayoutManager.ts +44 -23
- package/src/recyclerview/layout-managers/LayoutManager.ts +98 -20
- package/src/recyclerview/layout-managers/MasonryLayoutManager.ts +30 -8
- package/src/viewability/ViewabilityManager.ts +10 -6
|
@@ -116,13 +116,17 @@ const RecyclerViewComponent = <T,>(
|
|
|
116
116
|
// Initialize core RecyclerView manager and content offset management
|
|
117
117
|
const { recyclerViewManager, velocityTracker } =
|
|
118
118
|
useRecyclerViewManager(props);
|
|
119
|
-
const {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
const {
|
|
120
|
+
applyOffsetCorrection,
|
|
121
|
+
computeFirstVisibleIndexForOffsetCorrection,
|
|
122
|
+
applyInitialScrollIndex,
|
|
123
|
+
handlerMethods,
|
|
124
|
+
} = useRecyclerViewController(
|
|
125
|
+
recyclerViewManager,
|
|
126
|
+
ref,
|
|
127
|
+
scrollViewRef,
|
|
128
|
+
scrollAnchorRef
|
|
129
|
+
);
|
|
126
130
|
|
|
127
131
|
// Initialize view holder collection ref
|
|
128
132
|
const viewHolderCollectionRef = useRef<ViewHolderCollectionRef>(null);
|
|
@@ -211,7 +215,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
211
215
|
setRenderId((prev) => prev + 1);
|
|
212
216
|
} else {
|
|
213
217
|
viewHolderCollectionRef.current?.commitLayout();
|
|
214
|
-
|
|
218
|
+
applyOffsetCorrection();
|
|
215
219
|
}
|
|
216
220
|
});
|
|
217
221
|
|
|
@@ -247,6 +251,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
247
251
|
}
|
|
248
252
|
|
|
249
253
|
if (isMomentumEnd) {
|
|
254
|
+
computeFirstVisibleIndexForOffsetCorrection();
|
|
250
255
|
if (!recyclerViewManager.isOffsetProjectionEnabled) {
|
|
251
256
|
return;
|
|
252
257
|
}
|
|
@@ -272,6 +277,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
272
277
|
},
|
|
273
278
|
[
|
|
274
279
|
checkBounds,
|
|
280
|
+
computeFirstVisibleIndexForOffsetCorrection,
|
|
275
281
|
horizontal,
|
|
276
282
|
isHorizontalRTL,
|
|
277
283
|
recyclerViewManager,
|
|
@@ -439,6 +445,18 @@ const RecyclerViewComponent = <T,>(
|
|
|
439
445
|
);
|
|
440
446
|
}, [horizontal, shouldRenderFromBottom, adjustmentMinHeight]);
|
|
441
447
|
|
|
448
|
+
const scrollAnchor = useMemo(() => {
|
|
449
|
+
if (shouldMaintainVisibleContentPosition) {
|
|
450
|
+
return (
|
|
451
|
+
<ScrollAnchor
|
|
452
|
+
horizontal={Boolean(horizontal)}
|
|
453
|
+
scrollAnchorRef={scrollAnchorRef}
|
|
454
|
+
/>
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
return null;
|
|
458
|
+
}, [horizontal, shouldMaintainVisibleContentPosition]);
|
|
459
|
+
|
|
442
460
|
// console.log("render", recyclerViewManager.getRenderStack());
|
|
443
461
|
|
|
444
462
|
// Render the main RecyclerView structure
|
|
@@ -485,12 +503,7 @@ const RecyclerViewComponent = <T,>(
|
|
|
485
503
|
{...overrideProps}
|
|
486
504
|
>
|
|
487
505
|
{/* Scroll anchor for maintaining content position */}
|
|
488
|
-
{
|
|
489
|
-
<ScrollAnchor
|
|
490
|
-
horizontal={Boolean(horizontal)}
|
|
491
|
-
scrollAnchorRef={scrollAnchorRef}
|
|
492
|
-
/>
|
|
493
|
-
)}
|
|
506
|
+
{scrollAnchor}
|
|
494
507
|
{isHorizontalRTL && viewToMeasureBoundedSize}
|
|
495
508
|
{renderHeader}
|
|
496
509
|
{!isHorizontalRTL && viewToMeasureBoundedSize}
|
|
@@ -304,6 +304,12 @@ export class RecyclerViewManager<T> {
|
|
|
304
304
|
return this.updateScrollOffset(this.getAbsoluteLastScrollOffset());
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
restoreIfNeeded() {
|
|
308
|
+
if (this._isDisposed) {
|
|
309
|
+
this._isDisposed = false;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
307
313
|
dispose() {
|
|
308
314
|
this._isDisposed = true;
|
|
309
315
|
this.itemViewabilityManager.dispose();
|
|
@@ -26,6 +26,8 @@ export class RenderStackManager {
|
|
|
26
26
|
// Counter for generating unique sequential keys
|
|
27
27
|
private keyCounter: number;
|
|
28
28
|
|
|
29
|
+
private unProcessedIndices: Set<number>;
|
|
30
|
+
|
|
29
31
|
/**
|
|
30
32
|
* @param maxItemsInRecyclePool - Maximum number of items that can be in the recycle pool
|
|
31
33
|
*/
|
|
@@ -35,6 +37,7 @@ export class RenderStackManager {
|
|
|
35
37
|
this.keyMap = new Map();
|
|
36
38
|
this.stableIdMap = new Map();
|
|
37
39
|
this.keyCounter = 0;
|
|
40
|
+
this.unProcessedIndices = new Set();
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
/**
|
|
@@ -57,6 +60,7 @@ export class RenderStackManager {
|
|
|
57
60
|
dataLength: number
|
|
58
61
|
) {
|
|
59
62
|
this.clearRecyclePool();
|
|
63
|
+
this.unProcessedIndices.clear();
|
|
60
64
|
|
|
61
65
|
// Recycle keys for items that are no longer valid or visible
|
|
62
66
|
this.keyMap.forEach((keyInfo, key) => {
|
|
@@ -65,6 +69,9 @@ export class RenderStackManager {
|
|
|
65
69
|
this.recycleKey(key);
|
|
66
70
|
return;
|
|
67
71
|
}
|
|
72
|
+
if (!this.disableRecycling) {
|
|
73
|
+
this.unProcessedIndices.add(index);
|
|
74
|
+
}
|
|
68
75
|
if (!engagedIndices.includes(index)) {
|
|
69
76
|
this.recycleKey(key);
|
|
70
77
|
return;
|
|
@@ -114,7 +121,7 @@ export class RenderStackManager {
|
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
// Clean up stale items and manage the recycle pool size
|
|
117
|
-
this.cleanup(getStableId, engagedIndices, dataLength);
|
|
124
|
+
this.cleanup(getStableId, getItemType, engagedIndices, dataLength);
|
|
118
125
|
}
|
|
119
126
|
|
|
120
127
|
/**
|
|
@@ -131,6 +138,7 @@ export class RenderStackManager {
|
|
|
131
138
|
*/
|
|
132
139
|
private cleanup(
|
|
133
140
|
getStableId: (index: number) => string,
|
|
141
|
+
getItemType: (index: number) => string,
|
|
134
142
|
engagedIndices: ConsecutiveNumbers,
|
|
135
143
|
dataLength: number
|
|
136
144
|
) {
|
|
@@ -139,11 +147,27 @@ export class RenderStackManager {
|
|
|
139
147
|
// Remove items that are no longer in the dataset
|
|
140
148
|
for (const [key, keyInfo] of this.keyMap.entries()) {
|
|
141
149
|
const { index, itemType, stableId } = keyInfo;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
150
|
+
const indexOutOfBounds = index >= dataLength;
|
|
151
|
+
const hasStableIdChanged =
|
|
152
|
+
!indexOutOfBounds && getStableId(index) !== stableId;
|
|
153
|
+
|
|
154
|
+
if (indexOutOfBounds || hasStableIdChanged) {
|
|
155
|
+
const nextIndex = this.unProcessedIndices.values().next().value;
|
|
156
|
+
let shouldDeleteKey = true;
|
|
157
|
+
|
|
158
|
+
if (nextIndex !== undefined) {
|
|
159
|
+
const nextItemType = getItemType(nextIndex);
|
|
160
|
+
const nextStableId = getStableId(nextIndex);
|
|
161
|
+
if (itemType === nextItemType) {
|
|
162
|
+
this.syncItem(nextIndex, nextItemType, nextStableId);
|
|
163
|
+
shouldDeleteKey = false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (shouldDeleteKey) {
|
|
167
|
+
this.deleteKeyFromRecyclePool(itemType, key);
|
|
168
|
+
this.stableIdMap.delete(stableId);
|
|
169
|
+
itemsToDelete.push(key);
|
|
170
|
+
}
|
|
147
171
|
}
|
|
148
172
|
}
|
|
149
173
|
|
|
@@ -215,6 +239,8 @@ export class RenderStackManager {
|
|
|
215
239
|
this.getKeyFromRecyclePool(itemType) ||
|
|
216
240
|
this.generateKey();
|
|
217
241
|
|
|
242
|
+
this.unProcessedIndices.delete(index);
|
|
243
|
+
|
|
218
244
|
const keyInfo = this.keyMap.get(newKey);
|
|
219
245
|
if (keyInfo) {
|
|
220
246
|
// Update an existing key's metadata
|
|
@@ -2,6 +2,13 @@ import { useState, useCallback } from "react";
|
|
|
2
2
|
|
|
3
3
|
import { useRecyclerViewContext } from "../RecyclerViewContextProvider";
|
|
4
4
|
|
|
5
|
+
export type LayoutStateSetter<T> = (
|
|
6
|
+
newValue: T | ((prevValue: T) => T),
|
|
7
|
+
skipParentLayout?: boolean
|
|
8
|
+
) => void;
|
|
9
|
+
|
|
10
|
+
export type LayoutStateInitialValue<T> = T | (() => T);
|
|
11
|
+
|
|
5
12
|
/**
|
|
6
13
|
* Custom hook that combines state management with RecyclerView layout updates.
|
|
7
14
|
* This hook provides a way to manage state that affects the layout of the RecyclerView,
|
|
@@ -13,8 +20,8 @@ import { useRecyclerViewContext } from "../RecyclerViewContextProvider";
|
|
|
13
20
|
* - A setter function that updates the state and triggers a layout recalculation
|
|
14
21
|
*/
|
|
15
22
|
export function useLayoutState<T>(
|
|
16
|
-
initialState: T
|
|
17
|
-
): [T,
|
|
23
|
+
initialState: LayoutStateInitialValue<T>
|
|
24
|
+
): [T, LayoutStateSetter<T>] {
|
|
18
25
|
// Initialize state with the provided initial value
|
|
19
26
|
const [state, setState] = useState<T>(initialState);
|
|
20
27
|
// Get the RecyclerView context for layout management
|
|
@@ -28,16 +35,18 @@ export function useLayoutState<T>(
|
|
|
28
35
|
* @param newValue - Either a new state value or a function that receives the previous state
|
|
29
36
|
* and returns the new state
|
|
30
37
|
*/
|
|
31
|
-
const setLayoutState = useCallback(
|
|
32
|
-
(newValue
|
|
38
|
+
const setLayoutState: LayoutStateSetter<T> = useCallback(
|
|
39
|
+
(newValue, skipParentLayout) => {
|
|
33
40
|
// Update the state using either the new value or the result of the updater function
|
|
34
41
|
setState((prevValue) =>
|
|
35
42
|
typeof newValue === "function"
|
|
36
43
|
? (newValue as (prevValue: T) => T)(prevValue)
|
|
37
44
|
: newValue
|
|
38
45
|
);
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
if (!skipParentLayout) {
|
|
47
|
+
// Trigger a layout recalculation in the RecyclerView
|
|
48
|
+
recyclerViewContext?.layout();
|
|
49
|
+
}
|
|
41
50
|
},
|
|
42
51
|
[recyclerViewContext]
|
|
43
52
|
);
|