-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Minor adjustments to profiler #11376
Changes from 8 commits
bf8c304
d1976a9
53333ca
3a48ead
d0c4238
28d24c8
d1ec7f9
dbc20fd
737b5b2
1f0d2bd
88e2e47
d74c691
40a47f8
c29eb1d
874acf6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,14 +23,26 @@ export interface NextRenderOptions { | |
export interface ProfiledComponent<Props, Snapshot> | ||
extends React.FC<Props>, | ||
ProfiledComponentFields<Props, Snapshot>, | ||
ProfiledComponenOnlyFields<Props, Snapshot> {} | ||
ProfiledComponentOnlyFields<Props, Snapshot> {} | ||
|
||
interface UpdateSnapshot<Snapshot> { | ||
(newSnapshot: Snapshot): void; | ||
(updateSnapshot: (lastSnapshot: Readonly<Snapshot>) => Snapshot): void; | ||
} | ||
|
||
interface ProfiledComponenOnlyFields<Props, Snapshot> { | ||
interface SetSnapshot<Snapshot> { | ||
(partialSnapshot: Partial<Snapshot>): void; | ||
( | ||
updatePartialSnapshot: ( | ||
lastSnapshot: Readonly<Snapshot> | ||
) => Partial<Snapshot> | ||
): void; | ||
} | ||
|
||
interface ProfiledComponentOnlyFields<Props, Snapshot> { | ||
// Allows for partial updating of the snapshot by shallow merging the results | ||
setSnapshot: SetSnapshot<Snapshot>; | ||
// Performs a full replacement of the snapshot | ||
updateSnapshot: UpdateSnapshot<Snapshot>; | ||
} | ||
interface ProfiledComponentFields<Props, Snapshot> { | ||
|
@@ -54,21 +66,14 @@ interface ProfiledComponentFields<Props, Snapshot> { | |
*/ | ||
takeRender(options?: NextRenderOptions): Promise<Render<Snapshot>>; | ||
/** | ||
* Returns the current render count. | ||
* Returns the total number of renders. | ||
*/ | ||
currentRenderCount(): number; | ||
totalRenderCount(): number; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is now the only function that doesn't rely on the iterator position in its implementation. If possible, we should keep it that way to try and maintain the intent of the original API which should force you to iterate on each render in your tests. |
||
/** | ||
* Returns the current render. | ||
* @throws {Error} if no render has happened yet | ||
*/ | ||
getCurrentRender(): Render<Snapshot>; | ||
/** | ||
* Iterates the renders until the render count is reached. | ||
*/ | ||
takeUntilRenderCount( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This behaved in a way that was unintuitive due to the fact that it used the total render count rather than the iterator in its implementation. This isn't currently used, so I'd like us to try using the other methods first before determining if we need to reintroduce this. If we do reintroduce, I'd propose that instead of thinking about taking each render up to a specific count, that instead this method advances the iterator a certain number of renders from "current". Doing so would prevent weird situations like this: await Profiled.takeRender()
await Profiled.takeRender()
await Profiled.takeRender()
// This makes no sense since render #2 is now in the past
await Profiled.takeUntilRenderCount(2) |
||
count: number, | ||
optionsPerRender?: NextRenderOptions | ||
): Promise<void>; | ||
/** | ||
* Waits for the next render to happen. | ||
* Does not advance the render iterator. | ||
|
@@ -118,6 +123,16 @@ export function profile< | |
snapshotRef.current = snap; | ||
} | ||
}; | ||
|
||
const setSnapshot: SetSnapshot<Snapshot> = (partialSnapshot) => { | ||
updateSnapshot((snapshot) => ({ | ||
...snapshot, | ||
...(typeof partialSnapshot === "function" | ||
? partialSnapshot(snapshot) | ||
: partialSnapshot), | ||
})); | ||
}; | ||
|
||
const profilerOnRender: React.ProfilerOnRenderCallback = ( | ||
id, | ||
phase, | ||
|
@@ -179,28 +194,24 @@ export function profile< | |
), | ||
{ | ||
updateSnapshot, | ||
} satisfies ProfiledComponenOnlyFields<Props, Snapshot>, | ||
setSnapshot, | ||
} satisfies ProfiledComponentOnlyFields<Props, Snapshot>, | ||
{ | ||
renders: new Array< | ||
| Render<Snapshot> | ||
| { phase: "snapshotError"; count: number; error: unknown } | ||
>(), | ||
currentRenderCount() { | ||
totalRenderCount() { | ||
return Profiled.renders.length; | ||
}, | ||
async peekRender(options: NextRenderOptions = {}) { | ||
if (iteratorPosition < Profiled.renders.length) { | ||
const render = Profiled.renders[iteratorPosition]; | ||
if (render.phase === "snapshotError") { | ||
throw render.error; | ||
} | ||
return render; | ||
return this.getCurrentRender(); | ||
} | ||
const render = Profiled.waitForNextRender({ | ||
return Profiled.waitForNextRender({ | ||
[_stackTrace]: captureStackTrace(Profiled.peekRender), | ||
...options, | ||
}); | ||
return render; | ||
}, | ||
async takeRender(options: NextRenderOptions = {}) { | ||
let error: unknown = undefined; | ||
|
@@ -222,15 +233,12 @@ export function profile< | |
if (!currentRender) { | ||
throw new Error("Has not been rendered yet!"); | ||
} | ||
return currentRender; | ||
}, | ||
async takeUntilRenderCount( | ||
count: number, | ||
optionsPerRender?: NextRenderOptions | ||
) { | ||
while (Profiled.renders.length < count) { | ||
await Profiled.takeRender(optionsPerRender); | ||
|
||
const render = Profiled.renders[iteratorPosition]; | ||
if (render.phase === "snapshotError") { | ||
throw render.error; | ||
} | ||
return render; | ||
}, | ||
waitForNextRender({ | ||
timeout = 1000, | ||
|
@@ -322,7 +330,7 @@ export function profileHook<ReturnValue extends ValidSnapshot, Props>( | |
}, | ||
{ | ||
renders: ProfiledComponent.renders, | ||
currentSnapshotCount: ProfiledComponent.currentRenderCount, | ||
totalSnapshotCount: ProfiledComponent.totalRenderCount, | ||
async peekSnapshot(options) { | ||
return (await ProfiledComponent.peekRender(options)).snapshot; | ||
}, | ||
|
@@ -332,7 +340,6 @@ export function profileHook<ReturnValue extends ValidSnapshot, Props>( | |
getCurrentSnapshot() { | ||
return ProfiledComponent.getCurrentRender().snapshot; | ||
}, | ||
takeUntilSnapshotCount: ProfiledComponent.takeUntilRenderCount, | ||
async waitForNextSnapshot(options) { | ||
return (await ProfiledComponent.waitForNextRender(options)).snapshot; | ||
}, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a
setSnapshot
method which allows us now to set a partial snapshot, which gets merged with the full snapshot. I took the name from React's oldthis.setState()
method in class components, which has similar behavior.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the comment above - we might also consider the current behaviour, but
immer
-wrapped.