@shopify/flash-list 2.0.0-alpha.9 → 2.0.0-rc.2
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/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 +15 -8
- 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 +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 +486 -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/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/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 +63 -45
- 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 +21 -7
- package/dist/recyclerview/RecyclerViewManager.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.js +105 -113
- 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 +324 -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 +23 -8
- 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 +10 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.d.ts.map +1 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.js +39 -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 +19 -16
- package/dist/recyclerview/hooks/useBoundDetection.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 +3 -48
- package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.js +174 -123
- 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 +10 -1
- 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 +10 -16
- package/dist/recyclerview/layout-managers/LayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/LayoutManager.js +4 -14
- package/dist/recyclerview/layout-managers/LayoutManager.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 +1 -2
- 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 +20 -8
- package/src/FlashListRef.ts +320 -0
- package/src/MasonryFlashList.tsx +2 -2
- package/src/__tests__/RecyclerView.test.tsx +83 -29
- package/src/__tests__/RenderStackManager.test.ts +575 -0
- package/src/__tests__/helpers/createLayoutManager.ts +2 -3
- package/src/__tests__/useUnmountAwareCallbacks.test.tsx +12 -12
- package/src/benchmark/useFlatListBenchmark.ts +2 -2
- package/src/index.ts +1 -0
- 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 +82 -52
- package/src/recyclerview/RecyclerViewContextProvider.ts +12 -6
- package/src/recyclerview/RecyclerViewManager.ts +123 -98
- package/src/recyclerview/RenderStackManager.ts +291 -0
- package/src/recyclerview/ViewHolder.tsx +5 -3
- package/src/recyclerview/ViewHolderCollection.tsx +33 -12
- 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 +38 -0
- package/src/recyclerview/helpers/VelocityTracker.ts +77 -0
- package/src/recyclerview/hooks/useBoundDetection.ts +25 -18
- package/src/recyclerview/hooks/useMappingHelper.ts +1 -1
- package/src/recyclerview/hooks/useOnLoad.ts +4 -6
- package/src/recyclerview/hooks/useRecyclerViewController.tsx +199 -176
- package/src/recyclerview/hooks/useRecyclerViewManager.ts +11 -1
- 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 +12 -21
- package/src/viewability/ViewToken.ts +2 -2
- package/src/viewability/ViewabilityHelper.ts +1 -1
- package/src/viewability/ViewabilityManager.ts +6 -3
- 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
|
@@ -3,103 +3,88 @@ import ViewabilityManager from "../viewability/ViewabilityManager";
|
|
|
3
3
|
import { ConsecutiveNumbers } from "./helpers/ConsecutiveNumbers";
|
|
4
4
|
import { RVGridLayoutManagerImpl } from "./layout-managers/GridLayoutManager";
|
|
5
5
|
import {
|
|
6
|
+
LayoutParams,
|
|
6
7
|
RVDimension,
|
|
7
8
|
RVLayoutInfo,
|
|
8
9
|
RVLayoutManager,
|
|
10
|
+
SpanSizeInfo,
|
|
9
11
|
} from "./layout-managers/LayoutManager";
|
|
10
12
|
import { RVLinearLayoutManagerImpl } from "./layout-managers/LinearLayoutManager";
|
|
11
13
|
import { RVMasonryLayoutManagerImpl } from "./layout-managers/MasonryLayoutManager";
|
|
12
|
-
import { RecycleKeyManagerImpl, RecycleKeyManager } from "./RecycleKeyManager";
|
|
13
14
|
import { RecyclerViewProps } from "./RecyclerViewProps";
|
|
14
15
|
import {
|
|
15
16
|
RVEngagedIndicesTracker,
|
|
16
17
|
RVEngagedIndicesTrackerImpl,
|
|
17
18
|
Velocity,
|
|
18
19
|
} from "./helpers/EngagedIndicesTracker";
|
|
19
|
-
|
|
20
|
-
// Abstracts layout manager,
|
|
20
|
+
import { RenderStackManager } from "./RenderStackManager";
|
|
21
|
+
// Abstracts layout manager, render stack manager and viewability manager and generates render stack (progressively on load)
|
|
21
22
|
export class RecyclerViewManager<T> {
|
|
22
23
|
private initialDrawBatchSize = 1;
|
|
23
24
|
private engagedIndicesTracker: RVEngagedIndicesTracker;
|
|
24
|
-
private
|
|
25
|
+
private renderStackManager: RenderStackManager;
|
|
25
26
|
private layoutManager?: RVLayoutManager;
|
|
26
27
|
// Map of index to key
|
|
27
|
-
private renderStack: Map<number, string> = new Map();
|
|
28
28
|
private isFirstLayoutComplete = false;
|
|
29
29
|
private hasRenderedProgressively = false;
|
|
30
|
-
private
|
|
30
|
+
private propsRef: RecyclerViewProps<T>;
|
|
31
31
|
private itemViewabilityManager: ViewabilityManager<T>;
|
|
32
|
-
private
|
|
32
|
+
private _isDisposed = false;
|
|
33
|
+
private _isLayoutManagerDirty = false;
|
|
33
34
|
|
|
34
|
-
public disableRecycling = false;
|
|
35
35
|
public firstItemOffset = 0;
|
|
36
36
|
public ignoreScrollEvents = false;
|
|
37
37
|
|
|
38
|
+
public get isOffsetProjectionEnabled() {
|
|
39
|
+
return this.engagedIndicesTracker.enableOffsetProjection;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public get isDisposed() {
|
|
43
|
+
return this._isDisposed;
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
constructor(props: RecyclerViewProps<T>) {
|
|
39
|
-
this.
|
|
47
|
+
this.getStableId = this.getStableId.bind(this);
|
|
48
|
+
this.getItemType = this.getItemType.bind(this);
|
|
49
|
+
this.overrideItemLayout = this.overrideItemLayout.bind(this);
|
|
50
|
+
this.propsRef = props;
|
|
40
51
|
this.engagedIndicesTracker = new RVEngagedIndicesTrackerImpl();
|
|
41
|
-
this.
|
|
52
|
+
this.renderStackManager = new RenderStackManager(
|
|
53
|
+
props.maxItemsInRecyclePool
|
|
54
|
+
);
|
|
42
55
|
this.itemViewabilityManager = new ViewabilityManager<T>(this as any);
|
|
43
56
|
}
|
|
44
57
|
|
|
45
58
|
// updates render stack based on the engaged indices which are sorted. Recycles unused keys.
|
|
46
|
-
// TODO: Write comprehensive tests for this function
|
|
47
59
|
private updateRenderStack = (engagedIndices: ConsecutiveNumbers): void => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (this.disableRecycling) {
|
|
58
|
-
this.recycleKeyManager.clearPool();
|
|
59
|
-
}
|
|
60
|
-
for (const index of engagedIndices) {
|
|
61
|
-
const currentKey = this.renderStack.get(index);
|
|
62
|
-
|
|
63
|
-
if (
|
|
64
|
-
currentKey &&
|
|
65
|
-
!this.disableRecycling &&
|
|
66
|
-
!this.allocatedKeyTracker.has(currentKey)
|
|
67
|
-
) {
|
|
68
|
-
this.recycleKeyManager.recycleKey(currentKey);
|
|
69
|
-
}
|
|
60
|
+
this.renderStackManager.sync(
|
|
61
|
+
this.getStableId,
|
|
62
|
+
this.getItemType,
|
|
63
|
+
engagedIndices,
|
|
64
|
+
this.getDataLength()
|
|
65
|
+
);
|
|
66
|
+
};
|
|
70
67
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
currentKey
|
|
75
|
-
);
|
|
76
|
-
this.allocatedKeyTracker.add(newKey);
|
|
77
|
-
newRenderStack.set(index, newKey);
|
|
78
|
-
}
|
|
79
|
-
// DANGER
|
|
80
|
-
for (const [index, key] of this.renderStack) {
|
|
81
|
-
if (
|
|
82
|
-
this.recycleKeyManager.hasKeyInPool(key) &&
|
|
83
|
-
!newRenderStack.has(index) &&
|
|
84
|
-
index < (this.props.data?.length ?? 0)
|
|
85
|
-
) {
|
|
86
|
-
this.allocatedKeyTracker.add(key);
|
|
87
|
-
newRenderStack.set(index, key);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
68
|
+
get props() {
|
|
69
|
+
return this.propsRef;
|
|
70
|
+
}
|
|
90
71
|
|
|
91
|
-
|
|
92
|
-
|
|
72
|
+
setOffsetProjectionEnabled(value: boolean) {
|
|
73
|
+
this.engagedIndicesTracker.enableOffsetProjection = value;
|
|
74
|
+
}
|
|
93
75
|
|
|
94
76
|
updateProps(props: RecyclerViewProps<T>) {
|
|
95
|
-
this.
|
|
77
|
+
this.propsRef = props;
|
|
96
78
|
this.engagedIndicesTracker.drawDistance =
|
|
97
79
|
props.drawDistance ?? this.engagedIndicesTracker.drawDistance;
|
|
98
|
-
if (this.
|
|
80
|
+
if (this.propsRef.drawDistance === 0) {
|
|
99
81
|
this.initialDrawBatchSize = 1;
|
|
100
82
|
} else {
|
|
101
83
|
this.initialDrawBatchSize = (props.numColumns ?? 1) * 2;
|
|
102
84
|
}
|
|
85
|
+
this.initialDrawBatchSize =
|
|
86
|
+
this.propsRef.overrideProps?.initialDrawBatchSize ??
|
|
87
|
+
this.initialDrawBatchSize;
|
|
103
88
|
}
|
|
104
89
|
|
|
105
90
|
/**
|
|
@@ -111,7 +96,7 @@ export class RecyclerViewManager<T> {
|
|
|
111
96
|
offset: number,
|
|
112
97
|
velocity?: Velocity
|
|
113
98
|
): ConsecutiveNumbers | undefined {
|
|
114
|
-
if (this.layoutManager) {
|
|
99
|
+
if (this.layoutManager && !this._isDisposed) {
|
|
115
100
|
const engagedIndices = this.engagedIndicesTracker.updateScrollOffset(
|
|
116
101
|
offset - this.firstItemOffset,
|
|
117
102
|
velocity,
|
|
@@ -126,10 +111,18 @@ export class RecyclerViewManager<T> {
|
|
|
126
111
|
return undefined;
|
|
127
112
|
}
|
|
128
113
|
|
|
114
|
+
updateAverageRenderTime(time: number) {
|
|
115
|
+
this.engagedIndicesTracker.averageRenderTime = time;
|
|
116
|
+
}
|
|
117
|
+
|
|
129
118
|
getIsFirstLayoutComplete() {
|
|
130
119
|
return this.isFirstLayoutComplete;
|
|
131
120
|
}
|
|
132
121
|
|
|
122
|
+
disableRecycling(disable: boolean) {
|
|
123
|
+
this.renderStackManager.disableRecycling = disable;
|
|
124
|
+
}
|
|
125
|
+
|
|
133
126
|
getLayout(index: number) {
|
|
134
127
|
if (!this.layoutManager) {
|
|
135
128
|
throw new Error(
|
|
@@ -139,6 +132,17 @@ export class RecyclerViewManager<T> {
|
|
|
139
132
|
return this.layoutManager.getLayout(index);
|
|
140
133
|
}
|
|
141
134
|
|
|
135
|
+
tryGetLayout(index: number) {
|
|
136
|
+
if (
|
|
137
|
+
this.layoutManager &&
|
|
138
|
+
index >= 0 &&
|
|
139
|
+
index < this.layoutManager.getLayoutCount()
|
|
140
|
+
) {
|
|
141
|
+
return this.layoutManager.getLayout(index);
|
|
142
|
+
}
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
|
|
142
146
|
// Doesn't include header / foot etc
|
|
143
147
|
getChildContainerDimensions() {
|
|
144
148
|
if (!this.layoutManager) {
|
|
@@ -150,7 +154,7 @@ export class RecyclerViewManager<T> {
|
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
getRenderStack() {
|
|
153
|
-
return this.
|
|
157
|
+
return this.renderStackManager.getRenderStack();
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
getWindowSize() {
|
|
@@ -170,10 +174,10 @@ export class RecyclerViewManager<T> {
|
|
|
170
174
|
getMaxScrollOffset() {
|
|
171
175
|
return Math.max(
|
|
172
176
|
0,
|
|
173
|
-
(this.
|
|
177
|
+
(this.propsRef.horizontal
|
|
174
178
|
? this.getChildContainerDimensions().width
|
|
175
179
|
: this.getChildContainerDimensions().height) -
|
|
176
|
-
(this.
|
|
180
|
+
(this.propsRef.horizontal
|
|
177
181
|
? this.getWindowSize().width
|
|
178
182
|
: this.getWindowSize().height) +
|
|
179
183
|
this.firstItemOffset
|
|
@@ -189,46 +193,43 @@ export class RecyclerViewManager<T> {
|
|
|
189
193
|
this.engagedIndicesTracker.setScrollDirection(scrollDirection);
|
|
190
194
|
}
|
|
191
195
|
|
|
196
|
+
resetVelocityCompute() {
|
|
197
|
+
this.engagedIndicesTracker.resetVelocityHistory();
|
|
198
|
+
}
|
|
199
|
+
|
|
192
200
|
updateLayoutParams(windowSize: RVDimension, firstItemOffset: number) {
|
|
193
201
|
this.firstItemOffset = firstItemOffset;
|
|
194
202
|
const LayoutManagerClass = this.getLayoutManagerClass();
|
|
195
203
|
if (
|
|
196
204
|
this.layoutManager &&
|
|
197
205
|
Boolean(this.layoutManager?.isHorizontal()) !==
|
|
198
|
-
Boolean(this.
|
|
206
|
+
Boolean(this.propsRef.horizontal)
|
|
199
207
|
) {
|
|
200
208
|
throw new Error(
|
|
201
209
|
"Horizontal prop cannot be toggled, you can use a key on FlashList to recreate it."
|
|
202
210
|
);
|
|
203
211
|
}
|
|
212
|
+
if (this._isLayoutManagerDirty) {
|
|
213
|
+
this.layoutManager = undefined;
|
|
214
|
+
this._isLayoutManagerDirty = false;
|
|
215
|
+
}
|
|
216
|
+
const layoutManagerParams: LayoutParams = {
|
|
217
|
+
windowSize,
|
|
218
|
+
maxColumns: this.propsRef.numColumns ?? 1,
|
|
219
|
+
horizontal: Boolean(this.propsRef.horizontal),
|
|
220
|
+
optimizeItemArrangement: this.propsRef.optimizeItemArrangement ?? true,
|
|
221
|
+
overrideItemLayout: this.overrideItemLayout,
|
|
222
|
+
getItemType: this.getItemType,
|
|
223
|
+
};
|
|
204
224
|
if (!(this.layoutManager instanceof LayoutManagerClass)) {
|
|
205
225
|
// console.log("-----> new LayoutManagerClass");
|
|
206
226
|
|
|
207
227
|
this.layoutManager = new LayoutManagerClass(
|
|
208
|
-
|
|
209
|
-
windowSize,
|
|
210
|
-
maxColumns: this.props.numColumns ?? 1,
|
|
211
|
-
horizontal: Boolean(this.props.horizontal),
|
|
212
|
-
optimizeItemArrangement: this.props.optimizeItemArrangement ?? true,
|
|
213
|
-
overrideItemLayout: (index, layout) => {
|
|
214
|
-
this.props?.overrideItemLayout?.(
|
|
215
|
-
layout,
|
|
216
|
-
this.props.data![index],
|
|
217
|
-
index,
|
|
218
|
-
this.props.numColumns ?? 1,
|
|
219
|
-
this.props.extraData
|
|
220
|
-
);
|
|
221
|
-
},
|
|
222
|
-
},
|
|
228
|
+
layoutManagerParams,
|
|
223
229
|
this.layoutManager
|
|
224
230
|
);
|
|
225
231
|
} else {
|
|
226
|
-
this.layoutManager.updateLayoutParams(
|
|
227
|
-
windowSize,
|
|
228
|
-
maxColumns: this.props.numColumns ?? 1,
|
|
229
|
-
horizontal: Boolean(this.props.horizontal),
|
|
230
|
-
optimizeItemArrangement: this.props.optimizeItemArrangement ?? true,
|
|
231
|
-
});
|
|
232
|
+
this.layoutManager.updateLayoutParams(layoutManagerParams);
|
|
232
233
|
}
|
|
233
234
|
}
|
|
234
235
|
|
|
@@ -236,7 +237,7 @@ export class RecyclerViewManager<T> {
|
|
|
236
237
|
return this.layoutManager !== undefined;
|
|
237
238
|
}
|
|
238
239
|
|
|
239
|
-
|
|
240
|
+
computeVisibleIndices() {
|
|
240
241
|
if (!this.layoutManager) {
|
|
241
242
|
throw new Error(
|
|
242
243
|
"LayoutManager is not initialized, visible indices are not unavailable"
|
|
@@ -274,9 +275,9 @@ export class RecyclerViewManager<T> {
|
|
|
274
275
|
// Using higher buffer for masonry to avoid missing items
|
|
275
276
|
this.itemViewabilityManager.shouldListenToVisibleIndices &&
|
|
276
277
|
this.itemViewabilityManager.updateViewableItems(
|
|
277
|
-
this.
|
|
278
|
+
this.propsRef.masonry
|
|
278
279
|
? this.engagedIndicesTracker.getEngagedIndices().toArray()
|
|
279
|
-
: this.
|
|
280
|
+
: this.computeVisibleIndices().toArray()
|
|
280
281
|
);
|
|
281
282
|
}
|
|
282
283
|
|
|
@@ -290,7 +291,7 @@ export class RecyclerViewManager<T> {
|
|
|
290
291
|
|
|
291
292
|
processDataUpdate() {
|
|
292
293
|
if (this.hasLayout()) {
|
|
293
|
-
this.modifyChildrenLayout([], this.
|
|
294
|
+
this.modifyChildrenLayout([], this.propsRef.data?.length ?? 0);
|
|
294
295
|
if (!this.recomputeEngagedIndices()) {
|
|
295
296
|
// recomputeEngagedIndices will update the render stack if there are any changes in the engaged indices.
|
|
296
297
|
// It's important to update render stack so that elements are assgined right keys incase items were deleted.
|
|
@@ -304,33 +305,46 @@ export class RecyclerViewManager<T> {
|
|
|
304
305
|
}
|
|
305
306
|
|
|
306
307
|
dispose() {
|
|
308
|
+
this._isDisposed = true;
|
|
307
309
|
this.itemViewabilityManager.dispose();
|
|
308
310
|
}
|
|
309
311
|
|
|
312
|
+
markLayoutManagerDirty() {
|
|
313
|
+
this._isLayoutManagerDirty = true;
|
|
314
|
+
}
|
|
315
|
+
|
|
310
316
|
getInitialScrollIndex() {
|
|
311
317
|
return (
|
|
312
|
-
this.
|
|
313
|
-
(this.
|
|
318
|
+
this.propsRef.initialScrollIndex ??
|
|
319
|
+
(this.propsRef.maintainVisibleContentPosition?.startRenderingFromBottom
|
|
314
320
|
? this.getDataLength() - 1
|
|
315
321
|
: undefined)
|
|
316
322
|
);
|
|
317
323
|
}
|
|
318
324
|
|
|
325
|
+
shouldMaintainVisibleContentPosition() {
|
|
326
|
+
// Return true if maintainVisibleContentPosition is enabled and not horizontal
|
|
327
|
+
return (
|
|
328
|
+
!this.propsRef.maintainVisibleContentPosition?.disabled &&
|
|
329
|
+
!this.propsRef.horizontal
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
319
333
|
getDataLength() {
|
|
320
|
-
return this.
|
|
334
|
+
return this.propsRef.data?.length ?? 0;
|
|
321
335
|
}
|
|
322
336
|
|
|
323
337
|
private getLayoutManagerClass() {
|
|
324
338
|
// throw errors for incompatible props
|
|
325
|
-
if (this.
|
|
339
|
+
if (this.propsRef.masonry && this.propsRef.horizontal) {
|
|
326
340
|
throw new Error("Masonry and horizontal props are incompatible");
|
|
327
341
|
}
|
|
328
|
-
if ((this.
|
|
342
|
+
if ((this.propsRef.numColumns ?? 1) > 1 && this.propsRef.horizontal) {
|
|
329
343
|
throw new Error("numColumns and horizontal props are incompatible");
|
|
330
344
|
}
|
|
331
|
-
return this.
|
|
345
|
+
return this.propsRef.masonry
|
|
332
346
|
? RVMasonryLayoutManagerImpl
|
|
333
|
-
: (this.
|
|
347
|
+
: (this.propsRef.numColumns ?? 1) > 1 && !this.propsRef.horizontal
|
|
334
348
|
? RVGridLayoutManagerImpl
|
|
335
349
|
: RVLinearLayoutManagerImpl;
|
|
336
350
|
}
|
|
@@ -344,7 +358,7 @@ export class RecyclerViewManager<T> {
|
|
|
344
358
|
const initialItemLayout = this.layoutManager?.getLayout(
|
|
345
359
|
initialScrollIndex ?? 0
|
|
346
360
|
);
|
|
347
|
-
const initialItemOffset = this.
|
|
361
|
+
const initialItemOffset = this.propsRef.horizontal
|
|
348
362
|
? initialItemLayout?.x
|
|
349
363
|
: initialItemLayout?.y;
|
|
350
364
|
|
|
@@ -369,7 +383,7 @@ export class RecyclerViewManager<T> {
|
|
|
369
383
|
const layoutManager = this.layoutManager;
|
|
370
384
|
if (layoutManager) {
|
|
371
385
|
this.applyInitialScrollAdjustment();
|
|
372
|
-
const visibleIndices = this.
|
|
386
|
+
const visibleIndices = this.computeVisibleIndices();
|
|
373
387
|
// console.log("---------> visibleIndices", visibleIndices);
|
|
374
388
|
this.hasRenderedProgressively = visibleIndices.every(
|
|
375
389
|
(index) =>
|
|
@@ -390,7 +404,7 @@ export class RecyclerViewManager<T> {
|
|
|
390
404
|
0,
|
|
391
405
|
Math.min(
|
|
392
406
|
visibleIndices.length,
|
|
393
|
-
this.
|
|
407
|
+
this.getRenderStack().size + this.initialDrawBatchSize
|
|
394
408
|
)
|
|
395
409
|
)
|
|
396
410
|
);
|
|
@@ -399,14 +413,25 @@ export class RecyclerViewManager<T> {
|
|
|
399
413
|
|
|
400
414
|
private getItemType(index: number): string {
|
|
401
415
|
return (
|
|
402
|
-
this.
|
|
416
|
+
this.propsRef.getItemType?.(this.propsRef.data![index], index) ??
|
|
417
|
+
"default"
|
|
403
418
|
).toString();
|
|
404
419
|
}
|
|
405
420
|
|
|
406
421
|
private getStableId(index: number): string {
|
|
407
422
|
return (
|
|
408
|
-
this.
|
|
423
|
+
this.propsRef.keyExtractor?.(this.propsRef.data![index], index) ??
|
|
409
424
|
index.toString()
|
|
410
425
|
);
|
|
411
426
|
}
|
|
427
|
+
|
|
428
|
+
private overrideItemLayout(index: number, layout: SpanSizeInfo) {
|
|
429
|
+
this.propsRef?.overrideItemLayout?.(
|
|
430
|
+
layout,
|
|
431
|
+
this.propsRef.data![index],
|
|
432
|
+
index,
|
|
433
|
+
this.propsRef.numColumns ?? 1,
|
|
434
|
+
this.propsRef.extraData
|
|
435
|
+
);
|
|
436
|
+
}
|
|
412
437
|
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { ConsecutiveNumbers } from "./helpers/ConsecutiveNumbers";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Manages the recycling of rendered items in a virtualized list.
|
|
5
|
+
* This class handles tracking, recycling, and reusing item keys to optimize
|
|
6
|
+
* rendering performance by minimizing creation/destruction of components.
|
|
7
|
+
*/
|
|
8
|
+
export class RenderStackManager {
|
|
9
|
+
public disableRecycling = false;
|
|
10
|
+
|
|
11
|
+
// Maximum number of items that can be in the recycle pool
|
|
12
|
+
private maxItemsInRecyclePool: number;
|
|
13
|
+
|
|
14
|
+
// Stores pools of recycled keys for each item type
|
|
15
|
+
private recycleKeyPools: Map<string, Set<string>>;
|
|
16
|
+
|
|
17
|
+
// Maps active keys to their metadata (item type and stable ID)
|
|
18
|
+
private keyMap: Map<
|
|
19
|
+
string,
|
|
20
|
+
{ itemType: string; index: number; stableId: string }
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
// Maps stable IDs to their corresponding keys for quick lookups
|
|
24
|
+
private stableIdMap: Map<string, string>;
|
|
25
|
+
|
|
26
|
+
// Counter for generating unique sequential keys
|
|
27
|
+
private keyCounter: number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param maxItemsInRecyclePool - Maximum number of items that can be in the recycle pool
|
|
31
|
+
*/
|
|
32
|
+
constructor(maxItemsInRecyclePool: number = Number.MAX_SAFE_INTEGER) {
|
|
33
|
+
this.maxItemsInRecyclePool = maxItemsInRecyclePool;
|
|
34
|
+
this.recycleKeyPools = new Map();
|
|
35
|
+
this.keyMap = new Map();
|
|
36
|
+
this.stableIdMap = new Map();
|
|
37
|
+
this.keyCounter = 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Synchronizes the render stack with the current state of data.
|
|
42
|
+
* This method is the core orchestrator that:
|
|
43
|
+
* 1. Recycles keys for items that are no longer valid
|
|
44
|
+
* 2. Updates existing keys for items that remain visible
|
|
45
|
+
* 3. Assigns new keys for newly visible items
|
|
46
|
+
* 4. Cleans up excess items to maintain the recycling pool size
|
|
47
|
+
*
|
|
48
|
+
* @param getStableId - Function to get a stable identifier for an item at a specific index
|
|
49
|
+
* @param getItemType - Function to get the type of an item at a specific index
|
|
50
|
+
* @param engagedIndices - Collection of indices that are currently visible or engaged
|
|
51
|
+
* @param dataLength - Total length of the data set
|
|
52
|
+
*/
|
|
53
|
+
public sync(
|
|
54
|
+
getStableId: (index: number) => string,
|
|
55
|
+
getItemType: (index: number) => string,
|
|
56
|
+
engagedIndices: ConsecutiveNumbers,
|
|
57
|
+
dataLength: number
|
|
58
|
+
) {
|
|
59
|
+
this.clearRecyclePool();
|
|
60
|
+
|
|
61
|
+
// Recycle keys for items that are no longer valid or visible
|
|
62
|
+
this.keyMap.forEach((keyInfo, key) => {
|
|
63
|
+
const { index, stableId, itemType } = keyInfo;
|
|
64
|
+
if (index >= dataLength) {
|
|
65
|
+
this.recycleKey(key);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!engagedIndices.includes(index)) {
|
|
69
|
+
this.recycleKey(key);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const newStableId = getStableId(index);
|
|
73
|
+
const newItemType = getItemType(index);
|
|
74
|
+
if (stableId !== newStableId || itemType !== newItemType) {
|
|
75
|
+
this.recycleKey(key);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// First pass: process items that already have optimized keys
|
|
80
|
+
for (const index of engagedIndices) {
|
|
81
|
+
if (this.hasOptimizedKey(getStableId(index))) {
|
|
82
|
+
this.syncItem(index, getItemType(index), getStableId(index));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Second pass: process remaining items that need new keys
|
|
87
|
+
for (const index of engagedIndices) {
|
|
88
|
+
if (!this.hasOptimizedKey(getStableId(index))) {
|
|
89
|
+
this.syncItem(index, getItemType(index), getStableId(index));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// create indices that are not in the engagedIndices and less than dataLength
|
|
94
|
+
// select only indices that are not in the engagedIndices
|
|
95
|
+
const validIndicesInPool: number[] = [];
|
|
96
|
+
for (const keyInfo of this.keyMap.values()) {
|
|
97
|
+
const index = keyInfo.index;
|
|
98
|
+
if (index < dataLength && !engagedIndices.includes(index)) {
|
|
99
|
+
validIndicesInPool.push(index);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// First pass: process items that already have optimized keys
|
|
104
|
+
for (const index of validIndicesInPool) {
|
|
105
|
+
if (this.hasOptimizedKey(getStableId(index))) {
|
|
106
|
+
this.syncItem(index, getItemType(index), getStableId(index));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (const index of validIndicesInPool) {
|
|
111
|
+
if (!this.hasOptimizedKey(getStableId(index))) {
|
|
112
|
+
this.syncItem(index, getItemType(index), getStableId(index));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Clean up stale items and manage the recycle pool size
|
|
117
|
+
this.cleanup(getStableId, engagedIndices, dataLength);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Checks if a stable ID already has an assigned key
|
|
122
|
+
*/
|
|
123
|
+
private hasOptimizedKey(stableId: string): boolean {
|
|
124
|
+
return this.stableIdMap.has(stableId);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Cleans up stale keys and manages the recycle pool size.
|
|
129
|
+
* This ensures we don't maintain references to items that are no longer in the dataset,
|
|
130
|
+
* and limits the number of recycled items to avoid excessive memory usage.
|
|
131
|
+
*/
|
|
132
|
+
private cleanup(
|
|
133
|
+
getStableId: (index: number) => string,
|
|
134
|
+
engagedIndices: ConsecutiveNumbers,
|
|
135
|
+
dataLength: number
|
|
136
|
+
) {
|
|
137
|
+
const itemsToDelete = new Array<string>();
|
|
138
|
+
|
|
139
|
+
// Remove items that are no longer in the dataset
|
|
140
|
+
for (const [key, keyInfo] of this.keyMap.entries()) {
|
|
141
|
+
const { index, itemType, stableId } = keyInfo;
|
|
142
|
+
if (index >= dataLength || getStableId(index) !== stableId) {
|
|
143
|
+
// TODO: Find a way to reusue the key, instead of deleting it
|
|
144
|
+
this.deleteKeyFromRecyclePool(itemType, key);
|
|
145
|
+
this.stableIdMap.delete(stableId);
|
|
146
|
+
itemsToDelete.push(key);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (const key of itemsToDelete) {
|
|
151
|
+
this.keyMap.delete(key);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Limit the size of the recycle pool
|
|
155
|
+
const itemsRenderedForRecycling = this.keyMap.size - engagedIndices.length;
|
|
156
|
+
if (itemsRenderedForRecycling > this.maxItemsInRecyclePool) {
|
|
157
|
+
const deleteCount =
|
|
158
|
+
itemsRenderedForRecycling - this.maxItemsInRecyclePool;
|
|
159
|
+
let deleted = 0;
|
|
160
|
+
|
|
161
|
+
// Use a for loop so we can break early once we've deleted enough items
|
|
162
|
+
const entries = Array.from(this.keyMap.entries()).reverse();
|
|
163
|
+
for (let i = 0; i < entries.length && deleted < deleteCount; i++) {
|
|
164
|
+
const [key, keyInfo] = entries[i];
|
|
165
|
+
const { index, itemType, stableId } = keyInfo;
|
|
166
|
+
|
|
167
|
+
if (!engagedIndices.includes(index)) {
|
|
168
|
+
this.deleteKeyFromRecyclePool(itemType, key);
|
|
169
|
+
this.stableIdMap.delete(stableId);
|
|
170
|
+
this.keyMap.delete(key);
|
|
171
|
+
deleted++;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Places a key back into its type-specific recycle pool for future reuse
|
|
179
|
+
*/
|
|
180
|
+
private recycleKey(key: string): void {
|
|
181
|
+
if (this.disableRecycling) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const keyInfo = this.keyMap.get(key);
|
|
185
|
+
|
|
186
|
+
if (!keyInfo) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const { itemType } = keyInfo;
|
|
191
|
+
|
|
192
|
+
// Add key back to its type's pool
|
|
193
|
+
const pool = this.getRecyclePoolForType(itemType);
|
|
194
|
+
|
|
195
|
+
pool.add(key);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Returns the current render stack containing all active keys and their metadata
|
|
200
|
+
*/
|
|
201
|
+
public getRenderStack() {
|
|
202
|
+
return this.keyMap;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Syncs an individual item by assigning it an appropriate key.
|
|
207
|
+
* Will use an existing key if available, or generate a new one.
|
|
208
|
+
*
|
|
209
|
+
* @returns The key assigned to the item
|
|
210
|
+
*/
|
|
211
|
+
private syncItem(index: number, itemType: string, stableId: string): string {
|
|
212
|
+
// Try to reuse an existing key, or get one from the recycle pool, or generate a new one
|
|
213
|
+
const newKey =
|
|
214
|
+
this.stableIdMap.get(stableId) ||
|
|
215
|
+
this.getKeyFromRecyclePool(itemType) ||
|
|
216
|
+
this.generateKey();
|
|
217
|
+
|
|
218
|
+
const keyInfo = this.keyMap.get(newKey);
|
|
219
|
+
if (keyInfo) {
|
|
220
|
+
// Update an existing key's metadata
|
|
221
|
+
this.deleteKeyFromRecyclePool(itemType, newKey);
|
|
222
|
+
this.deleteKeyFromRecyclePool(keyInfo.itemType, newKey);
|
|
223
|
+
this.stableIdMap.delete(keyInfo.stableId);
|
|
224
|
+
keyInfo.index = index;
|
|
225
|
+
keyInfo.itemType = itemType;
|
|
226
|
+
keyInfo.stableId = stableId;
|
|
227
|
+
} else {
|
|
228
|
+
// Create a new entry in the key map
|
|
229
|
+
this.keyMap.set(newKey, {
|
|
230
|
+
itemType,
|
|
231
|
+
index,
|
|
232
|
+
stableId,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
this.stableIdMap.set(stableId, newKey);
|
|
236
|
+
|
|
237
|
+
return newKey;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Clears all recycled keys from the pool, effectively resetting the recycling system.
|
|
242
|
+
* This operation does not affect currently active keys.
|
|
243
|
+
*/
|
|
244
|
+
private clearRecyclePool() {
|
|
245
|
+
// iterate over all pools and clear them
|
|
246
|
+
for (const pool of this.recycleKeyPools.values()) {
|
|
247
|
+
pool.clear();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Generates a unique sequential key using an internal counter.
|
|
253
|
+
* @returns A unique key as a string
|
|
254
|
+
*/
|
|
255
|
+
private generateKey(): string {
|
|
256
|
+
return (this.keyCounter++).toString();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Removes a specific key from its type's recycle pool
|
|
261
|
+
*/
|
|
262
|
+
private deleteKeyFromRecyclePool(itemType: string, key: string) {
|
|
263
|
+
this.recycleKeyPools.get(itemType)?.delete(key);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Gets or creates a recycle pool for a specific item type
|
|
268
|
+
*/
|
|
269
|
+
private getRecyclePoolForType(itemType: string) {
|
|
270
|
+
let pool = this.recycleKeyPools.get(itemType);
|
|
271
|
+
if (!pool) {
|
|
272
|
+
pool = new Set();
|
|
273
|
+
this.recycleKeyPools.set(itemType, pool);
|
|
274
|
+
}
|
|
275
|
+
return pool;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Retrieves and removes a key from the type's recycle pool
|
|
280
|
+
* @returns A recycled key or undefined if none available
|
|
281
|
+
*/
|
|
282
|
+
private getKeyFromRecyclePool(itemType: string) {
|
|
283
|
+
const pool = this.getRecyclePoolForType(itemType);
|
|
284
|
+
if (pool.size > 0) {
|
|
285
|
+
const key = pool.values().next().value;
|
|
286
|
+
pool.delete(key);
|
|
287
|
+
return key;
|
|
288
|
+
}
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
}
|