Skip to content

Commit 75f75f3

Browse files
authored
Call Web Vitals reporting at correct time (#17933)
This PR adjust the Web Vitals reporting to be called *after* rendering occurs. It used to work like this, but React's render method is no longer synchronous—so we have to do it in an effect. Existing tests should cover this code path, and IMO it's unfeasible to test that it's invoked _after_ hydration. Also removed the `&& ST` condition which is not relevant to Web Vitals.
1 parent 4b126cc commit 75f75f3

File tree

3 files changed

+31
-11
lines changed

3 files changed

+31
-11
lines changed

packages/next/client/index.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -462,10 +462,6 @@ function renderReactElement(reactEl: JSX.Element, domEl: HTMLElement) {
462462
if (isInitialRender) {
463463
ReactDOM.hydrate(reactEl, domEl, markHydrateComplete)
464464
isInitialRender = false
465-
466-
if (onPerfEntry && ST) {
467-
measureWebVitals(onPerfEntry)
468-
}
469465
} else {
470466
ReactDOM.render(reactEl, domEl, markRenderComplete)
471467
}
@@ -744,5 +740,10 @@ function Root({
744740
}
745741
}, [])
746742
}
743+
// We should ask to measure the Web Vitals after rendering completes so we
744+
// don't cause any hydration delay:
745+
React.useEffect(() => {
746+
measureWebVitals(onPerfEntry)
747+
}, [])
747748
return children as React.ReactElement
748749
}

packages/next/client/performance-relayer.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,32 @@ import {
44
getFID,
55
getLCP,
66
getTTFB,
7+
Metric,
78
ReportHandler,
89
} from 'web-vitals'
910

10-
export default (onPerfEntry: ReportHandler) => {
11-
getCLS(onPerfEntry)
12-
getFID(onPerfEntry)
13-
getFCP(onPerfEntry)
14-
getLCP(onPerfEntry)
15-
getTTFB(onPerfEntry)
11+
let isRegistered = false
12+
let userReportHandler: ReportHandler | undefined
13+
14+
function onReport(metric: Metric) {
15+
if (userReportHandler) {
16+
userReportHandler(metric)
17+
}
18+
}
19+
20+
export default (onPerfEntry?: ReportHandler) => {
21+
// Update function if it changes:
22+
userReportHandler = onPerfEntry
23+
24+
// Only register listeners once:
25+
if (isRegistered) {
26+
return
27+
}
28+
isRegistered = true
29+
30+
getCLS(onReport)
31+
getFID(onReport)
32+
getFCP(onReport)
33+
getLCP(onReport)
34+
getTTFB(onReport)
1635
}

test/integration/build-output/test/index.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ describe('Build Output', () => {
104104
expect(parseFloat(err404FirstLoad) - 64.2).toBeLessThanOrEqual(0)
105105
expect(err404FirstLoad.endsWith('kB')).toBe(true)
106106

107-
expect(parseFloat(sharedByAll) - 60.7).toBeLessThanOrEqual(0)
107+
expect(parseFloat(sharedByAll) - 60.8).toBeLessThanOrEqual(0)
108108
expect(sharedByAll.endsWith('kB')).toBe(true)
109109

110110
if (_appSize.endsWith('kB')) {

0 commit comments

Comments
 (0)