react-render-profiler 0.1.0 → 0.2.0
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 +64 -90
- package/dist/index.d.ts +28 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +130 -91
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# React Render Profiler
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Profiler helpers built on React's `<Profiler>` callback with debounced aggregation output.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,127 +8,101 @@ A tiny profiler helper to measure initial render + rerender timings for React co
|
|
|
8
8
|
npm i react-render-profiler
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## What It Gives You
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
- Render samples from React `actualDuration` and `baseDuration`
|
|
14
|
+
- Aggregated reporting with debounce (`reportAfterMs`)
|
|
15
|
+
- Three integration styles:
|
|
16
|
+
- `withRenderProfiler(Component, options?)`
|
|
17
|
+
- `<RenderProfiler id="...">...</RenderProfiler>`
|
|
18
|
+
- `useRenderProfiler(componentName, options?)`
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
- approximate commit timing (`render start` -> `useEffect`)
|
|
17
|
-
- delayed aggregated reporting to keep console noise low
|
|
20
|
+
By default profiling is **disabled in production** (`NODE_ENV === "production"`).
|
|
18
21
|
|
|
19
|
-
## Usage
|
|
22
|
+
## Usage
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
import { withRenderProfiler } from 'react-render-profiler';
|
|
23
|
-
import { ProductCard } from './ProductCard';
|
|
24
|
-
|
|
25
|
-
export default withRenderProfiler(ProductCard, {
|
|
26
|
-
reportAfterMs: 5000,
|
|
27
|
-
logEachRender: false
|
|
28
|
-
});
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Actual usage patterns
|
|
32
|
-
|
|
33
|
-
### 1) Wrap component on export (global for all usages)
|
|
24
|
+
### HOC
|
|
34
25
|
|
|
35
26
|
```tsx
|
|
36
|
-
|
|
37
|
-
...
|
|
38
|
-
}
|
|
27
|
+
import { withRenderProfiler } from 'react-render-profiler';
|
|
39
28
|
|
|
40
|
-
|
|
41
|
-
componentName: '
|
|
29
|
+
const ProfiledCard = withRenderProfiler(ProductCard, {
|
|
30
|
+
componentName: 'ProductCard',
|
|
42
31
|
groupByComponent: true,
|
|
43
|
-
|
|
44
|
-
reportAfterMs: 5000,
|
|
32
|
+
reportAfterMs: 1000,
|
|
45
33
|
});
|
|
46
34
|
```
|
|
47
35
|
|
|
48
|
-
###
|
|
36
|
+
### Wrapper Component
|
|
49
37
|
|
|
50
38
|
```tsx
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## Measure only specific usage
|
|
63
|
-
|
|
64
|
-
For HOC usage, `enabled` can be a function of component props.
|
|
65
|
-
This lets you measure only specific places/instances and keep others unprofiled.
|
|
66
|
-
|
|
67
|
-
```tsx
|
|
68
|
-
type CardProps = { alias: string; profileRender?: boolean };
|
|
69
|
-
|
|
70
|
-
const ProductCardProfiled = withRenderProfiler<CardProps>(ProductCard, {
|
|
71
|
-
componentName: 'ProductCard',
|
|
72
|
-
groupByComponent: true,
|
|
73
|
-
enabled: (props) => props.profileRender === true,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// Only this usage is measured:
|
|
77
|
-
<ProductCardProfiled alias="a" profileRender />
|
|
78
|
-
|
|
79
|
-
// This usage is ignored by profiler:
|
|
80
|
-
<ProductCardProfiled alias="b" />
|
|
39
|
+
import { RenderProfiler } from 'react-render-profiler';
|
|
40
|
+
|
|
41
|
+
export function ProductSection() {
|
|
42
|
+
return (
|
|
43
|
+
<RenderProfiler id="ProductSection" groupByComponent reportAfterMs={1000}>
|
|
44
|
+
<ProductList />
|
|
45
|
+
</RenderProfiler>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
81
48
|
```
|
|
82
49
|
|
|
83
|
-
|
|
50
|
+
### Hook (manual `<Profiler>` placement)
|
|
84
51
|
|
|
85
52
|
```tsx
|
|
53
|
+
import { Profiler } from 'react';
|
|
86
54
|
import { useRenderProfiler } from 'react-render-profiler';
|
|
87
55
|
|
|
88
56
|
export function CheckoutSidebar() {
|
|
89
|
-
useRenderProfiler('CheckoutSidebar', {
|
|
90
|
-
|
|
57
|
+
const { profilerId, onRender, enabled } = useRenderProfiler('CheckoutSidebar', {
|
|
58
|
+
reportAfterMs: 1000,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!enabled) {
|
|
62
|
+
return <aside>...</aside>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<Profiler id={profilerId} onRender={onRender}>
|
|
67
|
+
<aside>...</aside>
|
|
68
|
+
</Profiler>
|
|
69
|
+
);
|
|
91
70
|
}
|
|
92
71
|
```
|
|
93
72
|
|
|
94
73
|
## API
|
|
95
74
|
|
|
96
|
-
### `
|
|
97
|
-
|
|
98
|
-
Wraps a component and reports stats to console after inactivity timeout.
|
|
99
|
-
|
|
100
|
-
### `useRenderProfiler(componentName, options?)`
|
|
75
|
+
### `RenderProfilerOptions<P>`
|
|
101
76
|
|
|
102
|
-
|
|
77
|
+
- `componentName?: string`
|
|
78
|
+
- `reportAfterMs?: number` (default `500`)
|
|
79
|
+
- `groupByComponent?: boolean` (default `false`)
|
|
80
|
+
- `log?: (rows: LogPayload[]) => void` (default `console.table` sink)
|
|
81
|
+
- `enabled?: boolean | ((props: P) => boolean)`
|
|
103
82
|
|
|
104
|
-
### `
|
|
83
|
+
### `LogPayload`
|
|
105
84
|
|
|
106
|
-
- `
|
|
107
|
-
- `
|
|
108
|
-
- `
|
|
109
|
-
- `
|
|
110
|
-
- `
|
|
85
|
+
- `componentName`
|
|
86
|
+
- `renders`
|
|
87
|
+
- `mountPhases`
|
|
88
|
+
- `updatePhases`
|
|
89
|
+
- `totalActualMs`
|
|
90
|
+
- `minActualMs`
|
|
91
|
+
- `maxActualMs`
|
|
92
|
+
- `totalBaseMs`
|
|
111
93
|
|
|
112
|
-
###
|
|
94
|
+
### `withRenderProfiler(Component, options?)`
|
|
113
95
|
|
|
114
|
-
|
|
115
|
-
- **Instance-level profiling**: `{ componentName, enabled: (props) => ..., reportAfterMs: 2000-5000 }`
|
|
96
|
+
Wraps a component in `<Profiler>`, supporting `enabled` as boolean or predicate function.
|
|
116
97
|
|
|
117
|
-
|
|
98
|
+
### `RenderProfiler`
|
|
118
99
|
|
|
119
|
-
|
|
120
|
-
- This helper measures render-to-effect timing, not full browser paint time.
|
|
100
|
+
Component form:
|
|
121
101
|
|
|
122
|
-
|
|
102
|
+
```tsx
|
|
103
|
+
<RenderProfiler id="MySection">{children}</RenderProfiler>
|
|
104
|
+
```
|
|
123
105
|
|
|
124
|
-
|
|
106
|
+
### `useRenderProfiler(componentName, options?)`
|
|
125
107
|
|
|
126
|
-
|
|
127
|
-
- `component` - component name used for profiling/grouping
|
|
128
|
-
- `renders` - total committed renders counted in this profile bucket
|
|
129
|
-
- `initialRenders` - number of first renders for mounted instances in this bucket
|
|
130
|
-
- `rerenders` - number of subsequent renders (`renders - initialRenders`)
|
|
131
|
-
- `totalMs` - sum of measured render durations in milliseconds
|
|
132
|
-
- `avgMs` - average render duration (`totalMs / renders`)
|
|
133
|
-
- `minMs` - fastest measured render duration in milliseconds
|
|
134
|
-
- `maxMs` - slowest measured render duration in milliseconds
|
|
108
|
+
Returns `{ profilerId, onRender, enabled }` for manual `<Profiler>` usage.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
type
|
|
3
|
-
component: string;
|
|
4
|
-
renders: number;
|
|
5
|
-
initialRenders: number;
|
|
6
|
-
rerenders: number;
|
|
7
|
-
totalMs: number;
|
|
8
|
-
avgMs: number;
|
|
9
|
-
minMs: number;
|
|
10
|
-
maxMs: number;
|
|
11
|
-
};
|
|
12
|
-
export type RenderProfilerOptions = {
|
|
1
|
+
import React, { type ProfilerOnRenderCallback } from 'react';
|
|
2
|
+
export type RenderProfilerOptions<P = unknown> = {
|
|
13
3
|
componentName?: string;
|
|
14
|
-
groupByComponent?: boolean;
|
|
15
4
|
reportAfterMs?: number;
|
|
16
|
-
|
|
5
|
+
groupByComponent?: boolean;
|
|
6
|
+
log?: (rows: LogPayload[]) => void;
|
|
7
|
+
enabled?: boolean | ((props: P) => boolean);
|
|
8
|
+
};
|
|
9
|
+
export type LogPayload = {
|
|
10
|
+
componentName: string;
|
|
11
|
+
renders: number;
|
|
12
|
+
mountPhases: number;
|
|
13
|
+
updatePhases: number;
|
|
14
|
+
totalActualMs: number;
|
|
15
|
+
minActualMs: number;
|
|
16
|
+
maxActualMs: number;
|
|
17
|
+
totalBaseMs: number;
|
|
18
|
+
};
|
|
19
|
+
export type RenderProfilerProps = {
|
|
20
|
+
id: string;
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
} & Omit<RenderProfilerOptions<never>, 'enabled'> & {
|
|
17
23
|
enabled?: boolean;
|
|
18
|
-
logger?: (label: string, payload: LogPayload) => void;
|
|
19
24
|
};
|
|
20
|
-
export
|
|
21
|
-
|
|
25
|
+
export declare function RenderProfiler({ id, children, reportAfterMs, groupByComponent, log, enabled, }: RenderProfilerProps): React.ReactElement;
|
|
26
|
+
export declare function useRenderProfiler(componentName: string, options?: Omit<RenderProfilerOptions<never>, 'enabled'> & {
|
|
27
|
+
enabled?: boolean;
|
|
28
|
+
}): {
|
|
29
|
+
profilerId: string;
|
|
30
|
+
onRender: ProfilerOnRenderCallback;
|
|
31
|
+
enabled: boolean;
|
|
22
32
|
};
|
|
23
|
-
export declare
|
|
24
|
-
export declare const withRenderProfiler: <P extends object>(WrappedComponent: React.ComponentType<P>, options?: RenderProfilerHOCOptions<P>) => React.FC<P>;
|
|
25
|
-
export {};
|
|
33
|
+
export declare function withRenderProfiler<P extends object>(WrappedComponent: React.ComponentType<P>, options?: RenderProfilerOptions<P>): React.FC<P>;
|
|
26
34
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAKZ,KAAK,wBAAwB,EAC9B,MAAM,OAAO,CAAC;AAEf,MAAM,MAAM,qBAAqB,CAAC,CAAC,GAAG,OAAO,IAAI;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AA8GF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,GAAG,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,GAAG;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,cAAc,CAAC,EAC7B,EAAE,EACF,QAAQ,EACR,aAAmB,EACnB,gBAAwB,EACxB,GAAmB,EACnB,OAAO,GACR,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,CA8B1C;AAED,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAClF;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,wBAAwB,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;CAClB,CAmCA;AAED,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EACjD,gBAAgB,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EACxC,OAAO,GAAE,qBAAqB,CAAC,CAAC,CAAM,GACrC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAuCb"}
|
package/dist/index.js
CHANGED
|
@@ -1,105 +1,144 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.withRenderProfiler = exports.useRenderProfiler = void 0;
|
|
3
|
+
exports.withRenderProfiler = exports.useRenderProfiler = exports.RenderProfiler = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const groupedStatsStore = new Map();
|
|
12
|
-
const createEmptyStats = () => ({
|
|
13
|
-
renders: 0,
|
|
14
|
-
initialRenders: 0,
|
|
15
|
-
rerenders: 0,
|
|
16
|
-
totalMs: 0,
|
|
17
|
-
minMs: Number.POSITIVE_INFINITY,
|
|
18
|
-
maxMs: 0,
|
|
19
|
-
timerId: null
|
|
20
|
-
});
|
|
21
|
-
const scheduleReport = (componentName, stats, reportAfterMs, logger) => {
|
|
22
|
-
if (stats.timerId) {
|
|
23
|
-
clearTimeout(stats.timerId);
|
|
6
|
+
const buckets = new Map();
|
|
7
|
+
let debounceTimer = null;
|
|
8
|
+
function defaultEnabledByEnv() {
|
|
9
|
+
if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'production') {
|
|
10
|
+
return false;
|
|
24
11
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
function round3(n) {
|
|
15
|
+
return Math.round(n * 1000) / 1000;
|
|
16
|
+
}
|
|
17
|
+
function touchBucket(key, displayName) {
|
|
18
|
+
let bucket = buckets.get(key);
|
|
19
|
+
if (!bucket) {
|
|
20
|
+
bucket = {
|
|
21
|
+
componentName: displayName,
|
|
22
|
+
renders: 0,
|
|
23
|
+
mountPhases: 0,
|
|
24
|
+
updatePhases: 0,
|
|
25
|
+
totalActualMs: 0,
|
|
26
|
+
minActualMs: Number.POSITIVE_INFINITY,
|
|
27
|
+
maxActualMs: 0,
|
|
28
|
+
totalBaseMs: 0,
|
|
36
29
|
};
|
|
37
|
-
|
|
38
|
-
}, reportAfterMs);
|
|
39
|
-
};
|
|
40
|
-
const useRenderProfiler = (componentName, options = {}) => {
|
|
41
|
-
const { groupByComponent = false, reportAfterMs = 3000, logEachRender = false, enabled = true, logger = defaultLogger } = options;
|
|
42
|
-
const renderStartRef = (0, react_1.useRef)(0);
|
|
43
|
-
const instanceRenderCountRef = (0, react_1.useRef)(0);
|
|
44
|
-
const statsRef = (0, react_1.useRef)(createEmptyStats());
|
|
45
|
-
if (groupByComponent && !groupedStatsStore.has(componentName)) {
|
|
46
|
-
groupedStatsStore.set(componentName, createEmptyStats());
|
|
30
|
+
buckets.set(key, bucket);
|
|
47
31
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
32
|
+
return bucket;
|
|
33
|
+
}
|
|
34
|
+
function defaultLogger(rows) {
|
|
35
|
+
const tableRows = rows.map((row) => ({
|
|
36
|
+
component: row.componentName,
|
|
37
|
+
renders: row.renders,
|
|
38
|
+
mounts: row.mountPhases,
|
|
39
|
+
updates: row.updatePhases,
|
|
40
|
+
totalMs: round3(row.totalActualMs),
|
|
41
|
+
minMs: row.renders ? round3(row.minActualMs) : 0,
|
|
42
|
+
maxMs: round3(row.maxActualMs),
|
|
43
|
+
avgMs: row.renders ? round3(row.totalActualMs / row.renders) : 0,
|
|
44
|
+
baseMs: round3(row.totalBaseMs),
|
|
45
|
+
}));
|
|
46
|
+
// eslint-disable-next-line no-console
|
|
47
|
+
console.table(tableRows);
|
|
48
|
+
}
|
|
49
|
+
function scheduleFlush(reportAfterMs, log) {
|
|
50
|
+
if (debounceTimer) {
|
|
51
|
+
clearTimeout(debounceTimer);
|
|
60
52
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
debounceTimer = setTimeout(() => {
|
|
54
|
+
debounceTimer = null;
|
|
55
|
+
const rows = [...buckets.values()].map((bucket) => ({ ...bucket }));
|
|
56
|
+
buckets.clear();
|
|
57
|
+
if (rows.length) {
|
|
58
|
+
log(rows);
|
|
64
59
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
60
|
+
}, reportAfterMs);
|
|
61
|
+
}
|
|
62
|
+
function normalizePhase(phase) {
|
|
63
|
+
return phase === 'mount' ? 'mount' : 'update';
|
|
64
|
+
}
|
|
65
|
+
function recordProfilerSample(aggregateKey, displayName, phase, actualDuration, baseDuration, reportAfterMs, log) {
|
|
66
|
+
const bucket = touchBucket(aggregateKey, displayName);
|
|
67
|
+
const normalizedPhase = normalizePhase(phase);
|
|
68
|
+
bucket.renders += 1;
|
|
69
|
+
if (normalizedPhase === 'mount') {
|
|
70
|
+
bucket.mountPhases += 1;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
bucket.updatePhases += 1;
|
|
74
|
+
}
|
|
75
|
+
bucket.totalActualMs += actualDuration;
|
|
76
|
+
bucket.minActualMs = Math.min(bucket.minActualMs, actualDuration);
|
|
77
|
+
bucket.maxActualMs = Math.max(bucket.maxActualMs, actualDuration);
|
|
78
|
+
bucket.totalBaseMs += baseDuration;
|
|
79
|
+
scheduleFlush(reportAfterMs, log);
|
|
80
|
+
}
|
|
81
|
+
function resolveEnabled(options, props) {
|
|
82
|
+
const { enabled } = options;
|
|
83
|
+
if (enabled === undefined) {
|
|
84
|
+
return defaultEnabledByEnv();
|
|
85
|
+
}
|
|
86
|
+
if (typeof enabled === 'function') {
|
|
87
|
+
return Boolean(enabled(props));
|
|
88
|
+
}
|
|
89
|
+
return enabled;
|
|
90
|
+
}
|
|
91
|
+
function RenderProfiler({ id, children, reportAfterMs = 500, groupByComponent = false, log = defaultLogger, enabled, }) {
|
|
92
|
+
const instanceId = (0, react_1.useId)();
|
|
93
|
+
const aggregateKey = groupByComponent ? id : `${id} (${instanceId})`;
|
|
94
|
+
const onRender = (0, react_1.useCallback)((_profilerId, phase, actualDuration, baseDuration) => {
|
|
95
|
+
recordProfilerSample(aggregateKey, id, phase, actualDuration, baseDuration, reportAfterMs, log);
|
|
96
|
+
}, [aggregateKey, id, log, reportAfterMs]);
|
|
97
|
+
const isEnabled = enabled === undefined ? defaultEnabledByEnv() : Boolean(enabled);
|
|
98
|
+
if (!isEnabled) {
|
|
99
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
|
|
100
|
+
}
|
|
101
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Profiler, { id: aggregateKey, onRender: onRender, children: children }));
|
|
102
|
+
}
|
|
103
|
+
exports.RenderProfiler = RenderProfiler;
|
|
104
|
+
function useRenderProfiler(componentName, options = {}) {
|
|
105
|
+
const { reportAfterMs = 500, groupByComponent = false, log = defaultLogger } = options;
|
|
106
|
+
const instanceId = (0, react_1.useId)();
|
|
107
|
+
const aggregateKey = (0, react_1.useMemo)(() => (groupByComponent ? componentName : `${componentName} (${instanceId})`), [componentName, groupByComponent, instanceId]);
|
|
108
|
+
const onRender = (0, react_1.useCallback)((_id, phase, actualDuration, baseDuration) => {
|
|
109
|
+
recordProfilerSample(aggregateKey, componentName, phase, actualDuration, baseDuration, reportAfterMs, log);
|
|
110
|
+
}, [aggregateKey, componentName, log, reportAfterMs]);
|
|
111
|
+
const enabledByOption = (0, react_1.useMemo)(() => {
|
|
112
|
+
if (options.enabled === undefined) {
|
|
113
|
+
return defaultEnabledByEnv();
|
|
82
114
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
};
|
|
115
|
+
return Boolean(options.enabled);
|
|
116
|
+
}, [options.enabled]);
|
|
117
|
+
return {
|
|
118
|
+
profilerId: aggregateKey,
|
|
119
|
+
onRender,
|
|
120
|
+
enabled: enabledByOption,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
92
123
|
exports.useRenderProfiler = useRenderProfiler;
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
(0,
|
|
99
|
-
|
|
124
|
+
function withRenderProfiler(WrappedComponent, options = {}) {
|
|
125
|
+
const fallbackName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
|
126
|
+
const wrappedName = options.componentName || fallbackName;
|
|
127
|
+
const Profiled = (props) => {
|
|
128
|
+
const { reportAfterMs = 500, groupByComponent = false, log = defaultLogger } = options;
|
|
129
|
+
const instanceId = (0, react_1.useId)();
|
|
130
|
+
const aggregateKey = groupByComponent ? wrappedName : `${wrappedName} (${instanceId})`;
|
|
131
|
+
const onRender = (0, react_1.useCallback)((_profilerId, phase, actualDuration, baseDuration) => {
|
|
132
|
+
recordProfilerSample(aggregateKey, wrappedName, phase, actualDuration, baseDuration, reportAfterMs, log);
|
|
133
|
+
}, [aggregateKey, log, reportAfterMs, wrappedName]);
|
|
134
|
+
const isEnabled = resolveEnabled(options, props);
|
|
135
|
+
if (!isEnabled) {
|
|
136
|
+
return (0, jsx_runtime_1.jsx)(WrappedComponent, { ...props });
|
|
137
|
+
}
|
|
138
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Profiler, { id: aggregateKey, onRender: onRender, children: (0, jsx_runtime_1.jsx)(WrappedComponent, { ...props }) }));
|
|
100
139
|
};
|
|
101
|
-
|
|
102
|
-
return
|
|
103
|
-
}
|
|
140
|
+
Profiled.displayName = `withRenderProfiler(${wrappedName})`;
|
|
141
|
+
return Profiled;
|
|
142
|
+
}
|
|
104
143
|
exports.withRenderProfiler = withRenderProfiler;
|
|
105
144
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":";;;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-render-profiler",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "React render profiling via Profiler API with debounced aggregated reporting",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -30,11 +30,14 @@
|
|
|
30
30
|
"react",
|
|
31
31
|
"performance",
|
|
32
32
|
"profiler",
|
|
33
|
+
"react-profiler",
|
|
33
34
|
"render",
|
|
34
|
-
"rerender"
|
|
35
|
+
"rerender",
|
|
36
|
+
"actualduration",
|
|
37
|
+
"baseduration"
|
|
35
38
|
],
|
|
36
39
|
"peerDependencies": {
|
|
37
|
-
"react": ">=
|
|
40
|
+
"react": ">=18.0.0"
|
|
38
41
|
},
|
|
39
42
|
"devDependencies": {
|
|
40
43
|
"@testing-library/jest-dom": "^6.9.1",
|