Skip to content

Commit a102767

Browse files
authored
Prevent infinite render loop (#1826)
* Convert maximum depth exceeded error to warning * v2.0.2
1 parent 8018f7a commit a102767

File tree

4 files changed

+22
-2
lines changed

4 files changed

+22
-2
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@shopify/flash-list",
3-
"version": "2.0.1",
3+
"version": "2.0.2",
44
"keywords": [
55
"react-native",
66
"recyclerview",

src/errors/WarningMessages.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
export const WarningMessages = {
22
keyExtractorNotDefinedForAnimation:
33
"keyExtractor is not defined. This might cause the animations to not work as expected.",
4+
exceededMaxRendersWithoutCommit:
5+
"Exceeded max renders without commit. This might mean that you have duplicate keys in your keyExtractor output or your list is nested in a ScrollView causing a lot of items to render at once. " +
6+
"If it's none of those and is causing a real issue or error, consider reporing this on FlashList Github",
47
};

src/recyclerview/RecyclerView.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121

2222
import { FlashListRef } from "../FlashListRef";
2323
import { ErrorMessages } from "../errors/ErrorMessages";
24+
import { WarningMessages } from "../errors/WarningMessages";
2425

2526
import { RVDimension } from "./layout-managers/LayoutManager";
2627
import {
@@ -209,8 +210,16 @@ const RecyclerViewComponent = <T,>(
209210
return { index, dimensions: layout };
210211
});
211212

213+
const hasExceededMaxRendersWithoutCommit =
214+
renderTimeTracker.hasExceededMaxRendersWithoutCommit();
215+
216+
if (hasExceededMaxRendersWithoutCommit) {
217+
console.warn(WarningMessages.exceededMaxRendersWithoutCommit);
218+
}
219+
212220
if (
213-
recyclerViewManager.modifyChildrenLayout(layoutInfo, data?.length ?? 0)
221+
recyclerViewManager.modifyChildrenLayout(layoutInfo, data?.length ?? 0) &&
222+
!hasExceededMaxRendersWithoutCommit
214223
) {
215224
// Trigger re-render if layout modifications were made
216225
setRenderId((prev) => prev + 1);

src/recyclerview/helpers/RenderTimeTracker.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ export class RenderTimeTracker {
66
private lastTimerStartedAt = -1;
77
private maxRenderTime = 32; // TODO: Improve this even more
88
private defaultRenderTime = 16;
9+
private rendersWithoutCommit = 0;
10+
private maxRendersWithoutCommit = 40;
911

1012
startTracking() {
13+
this.rendersWithoutCommit++;
1114
if (!PlatformConfig.trackAverageRenderTimeForOffsetProjection) {
1215
return;
1316
}
@@ -17,6 +20,7 @@ export class RenderTimeTracker {
1720
}
1821

1922
markRenderComplete() {
23+
this.rendersWithoutCommit = 0;
2024
if (!PlatformConfig.trackAverageRenderTimeForOffsetProjection) {
2125
return;
2226
}
@@ -26,6 +30,10 @@ export class RenderTimeTracker {
2630
}
2731
}
2832

33+
hasExceededMaxRendersWithoutCommit() {
34+
return this.rendersWithoutCommit >= this.maxRendersWithoutCommit;
35+
}
36+
2937
getRawValue() {
3038
return this.renderTimeAvgWindow.currentValue;
3139
}

0 commit comments

Comments
 (0)