Skip to content

Commit f0ab1b5

Browse files
committed
update
1 parent fb1b017 commit f0ab1b5

File tree

12 files changed

+183
-95
lines changed

12 files changed

+183
-95
lines changed

README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,14 @@ if (typeof window !== 'undefined') {
248248

249249
</details>
250250

251-
- `scan(options)`: Imperative API to start scanning
252-
- `useScan(options)`: Hook API to start scanning
253-
- `withScan(Component, options)`: Useful if you only want to scan a specific component
251+
- `scan(options: Options)`: Imperative API to start scanning
252+
- `useScan(options: Options)`: Hook API to start scanning
253+
- `withScan(Component, options: Options)`: Whitelist a specific component, do not scan other components
254+
- `getReport()`: Get a report of all the renders
255+
- `setOptions(options: Options): void`: Set options at runtime
256+
- `getOptions()`: Get the current options
257+
- `onRender(Component, onRender: (fiber: Fiber, render: Render) => void)`: Hook into a specific component's renders
258+
- `getRenderInfo(Component)`: Get the render info for a specific component
254259

255260
## Why React Scan?
256261

@@ -323,18 +328,18 @@ We expect all contributors to abide by the terms of our [Code of Conduct](https:
323328
- [x] Don't show label if no reconciliation occurred ("client renders" in DevTools)
324329
- [x] "global" counter using `sessionStorage`, aggregate count stats instead of immediate replacement
325330
- [x] Give a general report of the app's performance
326-
- [ ] checkbox filtering API, leaderboard
327-
- [ ] Offscreen canvas on worker thread
331+
- [x] Select areas of the screen to scan
332+
- [x] Report should include all renders
328333
- [x] heatmap decay (stacked renders will be more intense)
329-
- [ ] Investigate components (UI allowlist)
334+
- [x] Investigate components (UI allowlist)
335+
- [ ] Offscreen canvas on worker thread
330336
- [ ] UI for turning on/off options
331337
- [ ] “PageSpeed insights” for React
338+
- [ ] CLI
332339
- [ ] React Native support
340+
- [ ] Cleanup API reference
333341
- [ ] Name / explain the actual problem, docs
334342
- [ ] Simple FPS counter
335-
- [ ] Drag and select areas of the screen to scan
336-
- [ ] Long task progress bar filter
337-
- [x] Report should include all renders
338343
- [ ] [Runtime version guarding](https://github.com/lahmatiy/react-render-tracker/blob/229ad0e9c28853615300724d5dc86c140f250f60/src/publisher/react-integration/utils/getInternalReactConstants.ts#L28)
339344
- [ ] React as peer dependency (lock version to range)
340345
- [ ] Add a funny mascot, like the ["Stop I'm Changing" dude](https://www.youtube.com/shorts/FwOZdX7bDKI?app=desktop)

src/auto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import './core/instrumentation/placeholder';
1+
import './core/instrumentation/init';
22
import { scan } from './index';
33

44
if (typeof window !== 'undefined') {

src/core/index.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
type States,
1717
} from './web/inspect-element/inspect-state-machine';
1818
import { createToolbar } from './web/toolbar';
19+
import { getType } from './instrumentation/utils';
1920

2021
export interface Options {
2122
/**
@@ -106,6 +107,7 @@ export interface Internals {
106107
options: Options;
107108
scheduledOutlines: PendingOutline[];
108109
activeOutlines: ActiveOutline[];
110+
onRender: ((fiber: Fiber, render: Render) => void) | null;
109111
reportDataByFiber: WeakMap<
110112
Fiber,
111113
{
@@ -120,6 +122,7 @@ export interface Internals {
120122
{
121123
count: number;
122124
time: number;
125+
type: unknown;
123126
badRenders: Render[];
124127
}
125128
>;
@@ -252,6 +255,7 @@ export const ReactScanInternals = createStore<Internals>({
252255
report: undefined,
253256
alwaysShowLabels: false,
254257
},
258+
onRender: null,
255259
reportData: {},
256260
reportDataByFiber: new WeakMap(),
257261
scheduledOutlines: [],
@@ -277,15 +281,14 @@ export const start = () => {
277281
if (typeof window === 'undefined') {
278282
return;
279283
}
280-
const { options } = ReactScanInternals;
281284

282285
if (document.querySelector('react-scan-overlay')) return;
283286
initReactScanOverlay();
284287

285288
const overlayElement = document.createElement('react-scan-overlay') as any;
286289
document.documentElement.appendChild(overlayElement);
287290

288-
if (options.showToolbar) {
291+
if (ReactScanInternals.options.showToolbar) {
289292
createToolbar();
290293
}
291294
const ctx = overlayElement.getContext();
@@ -307,19 +310,19 @@ export const start = () => {
307310

308311
instrument({
309312
onCommitStart() {
310-
options.onCommitStart?.();
313+
ReactScanInternals.options.onCommitStart?.();
311314
},
312315
onRender(fiber, render) {
313316
if (ReactScanInternals.isPaused) {
314317
// don't draw if it's paused
315318
return;
316319
}
317-
options.onRender?.(fiber, render);
320+
ReactScanInternals.options.onRender?.(fiber, render);
318321
const outline = getOutline(fiber, render);
319322
if (!outline) return;
320323
ReactScanInternals.scheduledOutlines.push(outline);
321324

322-
if (options.playSound && audioContext) {
325+
if (ReactScanInternals.options.playSound && audioContext) {
323326
const renderTimeThreshold = 10;
324327
const amplitude = Math.min(
325328
1,
@@ -330,7 +333,7 @@ export const start = () => {
330333
flushOutlines(ctx, new Map());
331334
},
332335
onCommitFinish() {
333-
options.onCommitFinish?.();
336+
ReactScanInternals.options.onCommitFinish?.();
334337
},
335338
});
336339
};
@@ -370,3 +373,27 @@ export const useScan = (options: Options) => {
370373
scan(options);
371374
}, []);
372375
};
376+
377+
export const onRender = (
378+
type: unknown,
379+
_onRender: (fiber: Fiber, render: Render) => void,
380+
) => {
381+
const prevOnRender = ReactScanInternals.onRender;
382+
ReactScanInternals.onRender = (fiber, render) => {
383+
prevOnRender?.(fiber, render);
384+
if (getType(fiber.type) === type) {
385+
_onRender(fiber, render);
386+
}
387+
};
388+
};
389+
390+
export const getRenderInfo = (type: unknown) => {
391+
type = getType(type) || type;
392+
const reportData = ReactScanInternals.reportData;
393+
for (const componentName in reportData) {
394+
if (reportData[componentName].type === type) {
395+
return reportData[componentName];
396+
}
397+
}
398+
return null;
399+
};

src/core/instrumentation/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
traverseFiber,
1212
traverseState,
1313
} from './fiber';
14-
import { registerDevtoolsHook } from './placeholder';
14+
import { registerDevtoolsHook } from './init';
1515

1616
export interface Change {
1717
name: string;
@@ -150,6 +150,7 @@ export const reportRender = (
150150
count: (report?.count ?? 0) + 1,
151151
time: (report?.time ?? 0) + time,
152152
badRenders: report?.badRenders || [],
153+
type: getType(fiber.type) || fiber.type,
153154
};
154155
};
155156
export const reportRenderFiber = (fiber: Fiber, renders: (Render | null)[]) => {

src/core/instrumentation/placeholder.ts renamed to src/core/instrumentation/init.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import { type FiberRoot } from 'react-reconciler';
2-
import { NO_OP } from '../utils';
1+
import type { FiberRoot } from 'react-reconciler';
2+
3+
const NO_OP = () => {
4+
/**/
5+
};
36

47
export const registerDevtoolsHook = ({
58
onCommitFiberRoot,

src/core/utils.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import type { Render } from './instrumentation/index';
22

3-
export const NO_OP = () => {
4-
/**/
5-
};
6-
73
export const getLabelText = (renders: Render[]) => {
84
let labelText = '';
95

src/core/web/index.ts

Lines changed: 0 additions & 55 deletions
This file was deleted.

src/core/web/inspect-element/inspect-state-machine.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type Internals, ReactScanInternals } from '../../index';
22
import { throttle } from '../utils';
33
import { didFiberRender } from '../../instrumentation/fiber';
4+
import { restoreSizeFromLocalStorage } from '../toolbar';
45
import { renderPropsAndState } from './view-state';
56
import {
67
currentLockIconRect,
@@ -9,7 +10,6 @@ import {
910
updateCanvasSize,
1011
} from './overlay';
1112
import { getCompositeComponentFromElement, hasValidParent } from './utils';
12-
import { restoreSizeFromLocalStorage } from '../toolbar';
1313

1414
export type States =
1515
| {
@@ -260,7 +260,7 @@ export const createInspectElementStateMachine = () => {
260260
if (!document.contains(inspectState.focusedDomElement)) {
261261
setTimeout(() => {
262262

263-
// potential race condition solution for some websites
263+
// potential race condition solution for some websites
264264
clearCanvas();
265265
}, 500)
266266
inspectState.propContainer.style.maxHeight = '0';

src/core/web/inspect-element/overlay.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,13 @@ export const drawStatsPill = (
165165
const componentName = fiber
166166
? (getDisplayName(fiber) ?? 'Unknown')
167167
: 'Unknown';
168-
const text = `${componentName} • x${stats.count} (${stats.time.toFixed(1)}ms)`;
168+
let text = componentName;
169+
if (stats.count) {
170+
text += ` • ×${stats.count}`;
171+
if (stats.time) {
172+
text += ` (${stats.time.toFixed(1)}ms)`;
173+
}
174+
}
169175

170176
ctx.save();
171177
ctx.font = '12px system-ui, -apple-system, sans-serif';

0 commit comments

Comments
 (0)