Skip to content

Commit 786465d

Browse files
committed
feat: expose functionality to start benchmarking manually
1 parent 4b42163 commit 786465d

File tree

3 files changed

+103
-35
lines changed

3 files changed

+103
-35
lines changed

src/benchmark/useBenchmark.ts

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect } from "react";
1+
import React, { useEffect, useState, useCallback, useRef } from "react";
22

33
import FlashList from "../FlashList";
44

@@ -28,6 +28,12 @@ export interface BenchmarkParams {
2828
* Blank area is negative when list is able to draw faster than the scroll speed.
2929
*/
3030
sumNegativeBlankAreaValues?: boolean;
31+
32+
/**
33+
* When set to true, the benchmark will not start automatically.
34+
* Use the returned startBenchmark function to trigger it manually.
35+
*/
36+
startManually?: boolean;
3137
}
3238

3339
export interface BenchmarkResult {
@@ -54,15 +60,27 @@ export function useBenchmark(
5460
undefined,
5561
{ sumNegativeValues: params.sumNegativeBlankAreaValues, startDelayInMs: 0 }
5662
);
57-
useEffect(() => {
63+
const [isBenchmarkRunning, setIsBenchmarkRunning] = useState(false);
64+
const cancellableRef = useRef<Cancellable | null>(null);
65+
66+
const startBenchmark = useCallback(() => {
67+
if (isBenchmarkRunning) {
68+
return;
69+
}
70+
5871
const cancellable = new Cancellable();
72+
cancellableRef.current = cancellable;
5973
const suggestions: string[] = [];
74+
6075
if (flashListRef.current) {
6176
if (!(Number(flashListRef.current.props.data?.length) > 0)) {
6277
throw new Error("Data is empty, cannot run benchmark");
6378
}
6479
}
65-
const cancelTimeout = setTimeout(async () => {
80+
81+
setIsBenchmarkRunning(true);
82+
83+
const runBenchmark = async () => {
6684
const jsFPSMonitor = new JSFPSMonitor();
6785
jsFPSMonitor.startTracking();
6886
for (let i = 0; i < (params.repeatCount || 1); i++) {
@@ -89,14 +107,38 @@ export function useBenchmark(
89107
result.formattedString = getFormattedString(result);
90108
}
91109
callback(result);
110+
setIsBenchmarkRunning(false);
111+
};
112+
113+
runBenchmark();
114+
}, [
115+
blankAreaResult,
116+
callback,
117+
flashListRef,
118+
isBenchmarkRunning,
119+
params.repeatCount,
120+
params.speedMultiplier,
121+
]);
122+
123+
useEffect(() => {
124+
if (params.startManually) {
125+
return;
126+
}
127+
128+
const cancelTimeout = setTimeout(() => {
129+
startBenchmark();
92130
}, params.startDelayInMs || 3000);
131+
93132
return () => {
94133
clearTimeout(cancelTimeout);
95-
cancellable.cancel();
134+
if (cancellableRef.current) {
135+
cancellableRef.current.cancel();
136+
}
96137
};
97138
// eslint-disable-next-line react-hooks/exhaustive-deps
98139
}, []);
99-
return [blankAreaTracker];
140+
141+
return [blankAreaTracker, { startBenchmark, isBenchmarkRunning }] as const;
100142
}
101143

102144
export function getFormattedString(res: BenchmarkResult) {
@@ -151,15 +193,14 @@ async function runScrollBenchmark(
151193
): Promise<void> {
152194
if (flashListRef.current) {
153195
const horizontal = flashListRef.current.props.horizontal;
154-
const rlv = flashListRef.current.recyclerlistview_unsafe;
155-
if (rlv) {
156-
const rlvSize = rlv.getRenderedSize();
157-
const rlvContentSize = rlv.getContentDimension();
196+
const windowSize = flashListRef.current.getWindowSize();
197+
const contentSize = flashListRef.current.getChildContainerDimensions();
158198

199+
if (windowSize && contentSize) {
159200
const fromX = 0;
160201
const fromY = 0;
161-
const toX = rlvContentSize.width - rlvSize.width;
162-
const toY = rlvContentSize.height - rlvSize.height;
202+
const toX = contentSize.width - windowSize.width;
203+
const toY = contentSize.height - windowSize.height;
163204

164205
const scrollNow = (x: number, y: number) => {
165206
flashListRef.current?.scrollToOffset({

src/benchmark/useBlankAreaTracker.ts

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React, { useCallback, useRef } from "react";
2-
import { RecyclerListView, RecyclerListViewProps } from "recyclerlistview";
32

43
import { BlankAreaEvent } from "../native/auto-layout/AutoLayoutView";
54
import FlashList from "../FlashList";
@@ -61,18 +60,13 @@ export function useBlankAreaTracker(
6160
}
6261
return;
6362
}
64-
const rlv = flashListRef.current?.recyclerlistview_unsafe;
65-
const horizontal = Boolean(flashListRef.current?.props.horizontal);
66-
if (rlv) {
67-
processBlankAreaChange(
68-
rlv,
69-
horizontal,
70-
blankAreaResult,
71-
event,
72-
onBlankAreaChangeRef.current,
73-
config
74-
);
75-
}
63+
processBlankAreaChange(
64+
flashListRef.current,
65+
blankAreaResult,
66+
event,
67+
onBlankAreaChangeRef.current,
68+
config
69+
);
7670
},
7771
// eslint-disable-next-line react-hooks/exhaustive-deps
7872
[flashListRef]
@@ -81,19 +75,19 @@ export function useBlankAreaTracker(
8175
}
8276

8377
function processBlankAreaChange(
84-
rlv: RecyclerListView<RecyclerListViewProps, any>,
85-
horizontal: boolean,
78+
ref: FlashList<any> | null,
8679
blankAreaResult: BlankAreaTrackerResult,
8780
event: BlankAreaEvent,
8881
onBlankAreaChange?: (value: BlankAreaTrackerResult) => void,
8982
config?: BlankAreaTrackerConfig
9083
) {
84+
const horizontal = Boolean(ref?.props.horizontal);
9185
const listSize = horizontal
92-
? rlv.getRenderedSize().width
93-
: rlv.getRenderedSize().height;
86+
? ref?.getWindowSize()?.width ?? 0
87+
: ref?.getWindowSize()?.height ?? 0;
9488
const contentSize = horizontal
95-
? rlv.getContentDimension().width
96-
: rlv.getContentDimension().height;
89+
? ref?.getChildContainerDimensions()?.width ?? 0
90+
: ref?.getChildContainerDimensions()?.height ?? 0;
9791

9892
// ignores blank events when there isn't enough content to fill the list
9993
if (contentSize > listSize) {

src/benchmark/useFlatListBenchmark.ts

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect } from "react";
1+
import { useCallback, useEffect, useRef, useState } from "react";
22
import { FlatList } from "react-native";
33

44
import { autoScroll, Cancellable } from "./AutoScrollHelper";
@@ -23,14 +23,24 @@ export function useFlatListBenchmark(
2323
callback: (benchmarkResult: BenchmarkResult) => void,
2424
params: FlatListBenchmarkParams
2525
) {
26-
useEffect(() => {
26+
const [isBenchmarkRunning, setIsBenchmarkRunning] = useState(false);
27+
const cancellableRef = useRef<Cancellable | null>(null);
28+
29+
const startBenchmark = useCallback(() => {
30+
if (isBenchmarkRunning) {
31+
return;
32+
}
2733
const cancellable = new Cancellable();
34+
cancellableRef.current = cancellable;
2835
if (flatListRef.current && flatListRef.current.props) {
2936
if (!(Number(flatListRef.current.props.data?.length) > 0)) {
3037
throw new Error("Data is empty, cannot run benchmark");
3138
}
3239
}
33-
const cancelTimeout = setTimeout(async () => {
40+
41+
setIsBenchmarkRunning(true);
42+
43+
const runBenchmark = async () => {
3444
const jsFPSMonitor = new JSFPSMonitor();
3545
jsFPSMonitor.startTracking();
3646
for (let i = 0; i < (params.repeatCount || 1); i++) {
@@ -51,14 +61,37 @@ export function useFlatListBenchmark(
5161
result.formattedString = getFormattedString(result);
5262
}
5363
callback(result);
64+
setIsBenchmarkRunning(false);
65+
};
66+
67+
runBenchmark();
68+
}, [
69+
callback,
70+
flatListRef,
71+
isBenchmarkRunning,
72+
params.repeatCount,
73+
params.speedMultiplier,
74+
params.targetOffset,
75+
]);
76+
77+
useEffect(() => {
78+
if (params.startManually) {
79+
return;
80+
}
81+
82+
const cancelTimeout = setTimeout(() => {
83+
startBenchmark();
5484
}, params.startDelayInMs || 3000);
85+
5586
return () => {
5687
clearTimeout(cancelTimeout);
57-
cancellable.cancel();
88+
if (cancellableRef.current) {
89+
cancellableRef.current.cancel();
90+
}
5891
};
5992
// eslint-disable-next-line react-hooks/exhaustive-deps
6093
}, []);
61-
return [];
94+
return { startBenchmark, isBenchmarkRunning };
6295
}
6396

6497
/**

0 commit comments

Comments
 (0)