Skip to content

Commit 5ad1b81

Browse files
committed
better abstractions
1 parent 05e8319 commit 5ad1b81

File tree

6 files changed

+433
-728
lines changed

6 files changed

+433
-728
lines changed

examples/benchmark-react/src/baseline/index.tsx

Lines changed: 62 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
1+
import {
2+
onProfilerRender,
3+
useBenchState,
4+
waitForPaint,
5+
} from '@shared/benchHarness';
16
import { ItemRow } from '@shared/components';
27
import {
3-
FIXTURE_AUTHORS,
8+
FIXTURE_AUTHORS_BY_ID,
49
FIXTURE_ITEMS,
510
generateFreshData,
611
} from '@shared/data';
7-
import { captureSnapshot, getReport, registerRefs } from '@shared/refStability';
12+
import { registerRefs } from '@shared/refStability';
813
import type { Item, UpdateAuthorOptions } from '@shared/types';
9-
import React, {
10-
useCallback,
11-
useContext,
12-
useEffect,
13-
useMemo,
14-
useRef,
15-
useState,
16-
} from 'react';
14+
import React, { useCallback, useContext, useMemo, useState } from 'react';
1715
import { createRoot } from 'react-dom/client';
1816

1917
const ItemsContext = React.createContext<{
@@ -46,134 +44,90 @@ function SortedListView() {
4644

4745
function BenchmarkHarness() {
4846
const [items, setItems] = useState<Item[]>([]);
49-
const [ids, setIds] = useState<string[]>([]);
50-
const [showSortedView, setShowSortedView] = useState(false);
51-
const containerRef = useRef<HTMLDivElement>(null);
52-
const completeResolveRef = useRef<(() => void) | null>(null);
53-
54-
const setComplete = useCallback(() => {
55-
completeResolveRef.current?.();
56-
completeResolveRef.current = null;
57-
containerRef.current?.setAttribute('data-bench-complete', 'true');
58-
}, []);
47+
const {
48+
ids,
49+
showSortedView,
50+
containerRef,
51+
measureMount,
52+
measureUpdate,
53+
measureUpdateWithDelay,
54+
setComplete,
55+
completeResolveRef,
56+
setIds,
57+
setShowSortedView,
58+
unmountAll: unmountBase,
59+
registerAPI,
60+
} = useBenchState();
5961

6062
const mount = useCallback(
6163
(n: number) => {
62-
performance.mark('mount-start');
6364
const sliced = FIXTURE_ITEMS.slice(0, n);
64-
setItems(sliced);
65-
setIds(sliced.map(i => i.id));
66-
requestAnimationFrame(() => {
67-
requestAnimationFrame(() => {
68-
performance.mark('mount-end');
69-
performance.measure('mount-duration', 'mount-start', 'mount-end');
70-
setComplete();
71-
});
65+
const slicedIds = sliced.map(i => i.id);
66+
measureMount(() => {
67+
setItems(sliced);
68+
setIds(slicedIds);
7269
});
7370
},
74-
[setComplete],
71+
[measureMount, setIds],
7572
);
7673

7774
const updateEntity = useCallback(
7875
(id: string) => {
79-
performance.mark('update-start');
80-
setItems(prev =>
81-
prev.map(item =>
82-
item.id === id ? { ...item, label: `${item.label} (updated)` } : item,
83-
),
84-
);
85-
requestAnimationFrame(() => {
86-
requestAnimationFrame(() => {
87-
performance.mark('update-end');
88-
performance.measure('update-duration', 'update-start', 'update-end');
89-
setComplete();
90-
});
76+
measureUpdate(() => {
77+
setItems(prev =>
78+
prev.map(item =>
79+
item.id === id ?
80+
{ ...item, label: `${item.label} (updated)` }
81+
: item,
82+
),
83+
);
9184
});
9285
},
93-
[setComplete],
86+
[measureUpdate],
9487
);
9588

9689
const updateAuthor = useCallback(
9790
(authorId: string, options?: UpdateAuthorOptions) => {
98-
performance.mark('update-start');
99-
const delayMs = options?.simulateNetworkDelayMs ?? 0;
100-
const requestCount = options?.simulatedRequestCount ?? 1;
101-
const totalDelayMs = delayMs * requestCount;
102-
103-
const doUpdate = () => {
104-
const author = FIXTURE_AUTHORS.find(a => a.id === authorId);
105-
if (author) {
106-
const newAuthor = {
107-
...author,
108-
name: `${author.name} (updated)`,
109-
};
110-
setItems(prev =>
111-
prev.map(item =>
112-
item.author.id === authorId ?
113-
{ ...item, author: newAuthor }
114-
: item,
115-
),
116-
);
117-
}
118-
requestAnimationFrame(() => {
119-
requestAnimationFrame(() => {
120-
performance.mark('update-end');
121-
performance.measure(
122-
'update-duration',
123-
'update-start',
124-
'update-end',
125-
);
126-
setComplete();
127-
});
128-
});
129-
};
130-
131-
if (totalDelayMs > 0) {
132-
setTimeout(doUpdate, totalDelayMs);
133-
} else {
134-
doUpdate();
135-
}
91+
const author = FIXTURE_AUTHORS_BY_ID.get(authorId);
92+
if (!author) return;
93+
const newAuthor = { ...author, name: `${author.name} (updated)` };
94+
measureUpdateWithDelay(options, () => {
95+
setItems(prev =>
96+
prev.map(item =>
97+
item.author.id === authorId ? { ...item, author: newAuthor } : item,
98+
),
99+
);
100+
});
136101
},
137-
[setComplete],
102+
[measureUpdateWithDelay],
138103
);
139104

140105
const unmountAll = useCallback(() => {
141-
setIds([]);
106+
unmountBase();
142107
setItems([]);
143-
}, []);
108+
}, [unmountBase]);
144109

145110
const bulkIngest = useCallback(
146111
(n: number) => {
147-
performance.mark('mount-start');
148112
const { items: freshItems } = generateFreshData(n);
149-
setItems(freshItems);
150-
setIds(freshItems.map(i => i.id));
151-
requestAnimationFrame(() => {
152-
requestAnimationFrame(() => {
153-
performance.mark('mount-end');
154-
performance.measure('mount-duration', 'mount-start', 'mount-end');
155-
setComplete();
156-
});
113+
const freshIds = freshItems.map(i => i.id);
114+
measureMount(() => {
115+
setItems(freshItems);
116+
setIds(freshIds);
157117
});
158118
},
159-
[setComplete],
119+
[measureMount, setIds],
160120
);
161121

162122
const mountSortedView = useCallback(
163123
(n: number) => {
164-
performance.mark('mount-start');
165124
const sliced = FIXTURE_ITEMS.slice(0, n);
166-
setItems(sliced);
167-
setShowSortedView(true);
168-
requestAnimationFrame(() => {
169-
requestAnimationFrame(() => {
170-
performance.mark('mount-end');
171-
performance.measure('mount-duration', 'mount-start', 'mount-end');
172-
setComplete();
173-
});
125+
measureMount(() => {
126+
setItems(sliced);
127+
setShowSortedView(true);
174128
});
175129
},
176-
[setComplete],
130+
[measureMount, setShowSortedView],
177131
);
178132

179133
const mountUnmountCycle = useCallback(
@@ -185,55 +139,22 @@ function BenchmarkHarness() {
185139
mount(n);
186140
await p;
187141
unmountAll();
188-
await new Promise<void>(r =>
189-
requestAnimationFrame(() => requestAnimationFrame(() => r())),
190-
);
142+
await waitForPaint();
191143
}
192144
setComplete();
193145
},
194-
[mount, unmountAll, setComplete],
146+
[mount, unmountAll, setComplete, completeResolveRef],
195147
);
196148

197-
const getRenderedCount = useCallback(() => ids.length, [ids]);
198-
199-
const captureRefSnapshot = useCallback(() => {
200-
captureSnapshot();
201-
}, []);
202-
203-
const getRefStabilityReport = useCallback(() => getReport(), []);
204-
205-
useEffect(() => {
206-
window.__BENCH__ = {
207-
mount,
208-
updateEntity,
209-
updateAuthor,
210-
unmountAll,
211-
getRenderedCount,
212-
captureRefSnapshot,
213-
getRefStabilityReport,
214-
mountUnmountCycle,
215-
bulkIngest,
216-
mountSortedView,
217-
};
218-
return () => {
219-
delete window.__BENCH__;
220-
};
221-
}, [
149+
registerAPI({
222150
mount,
223151
updateEntity,
224152
updateAuthor,
225153
unmountAll,
226154
mountUnmountCycle,
227155
bulkIngest,
228156
mountSortedView,
229-
getRenderedCount,
230-
captureRefSnapshot,
231-
getRefStabilityReport,
232-
]);
233-
234-
useEffect(() => {
235-
document.body.setAttribute('data-app-ready', 'true');
236-
}, []);
157+
});
237158

238159
return (
239160
<ItemsContext.Provider value={{ items, setItems }}>
@@ -249,17 +170,6 @@ function BenchmarkHarness() {
249170
);
250171
}
251172

252-
function onProfilerRender(
253-
_id: string,
254-
phase: 'mount' | 'update' | 'nested-update',
255-
actualDuration: number,
256-
) {
257-
performance.measure(`react-commit-${phase}`, {
258-
start: performance.now() - actualDuration,
259-
duration: actualDuration,
260-
});
261-
}
262-
263173
const rootEl = document.getElementById('root') ?? document.body;
264174
createRoot(rootEl).render(
265175
<React.Profiler id="bench" onRender={onProfilerRender}>

0 commit comments

Comments
 (0)