Put perf integration behind a feature flag (#11455)
* Enable User Timing API integration with a feature flag * Expose a way to toggle user timing flag in www * Update ReactNativeCSFeatureFlags.js * Update ReactFeatureFlags.js
Dan Abramov committed
Nov 6, 2017 at 16:07 UTC
3b27160f82d9934f7ea9ddf064424634526702ca
10 files changed
+501
-498
packages/react-cs-renderer/src/ReactNativeCSFeatureFlags.js
+8
@@ -7,6 +7,8 @@
7
* @flow
8
*/
9
10
+import invariant from 'fbjs/lib/invariant';
11
+
12
import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
13
import typeof * as CSFeatureFlagsType from './ReactNativeCSFeatureFlags';
14
@@ -14,12 +16,18 @@ export const enableAsyncSubtreeAPI = true;
16
export const enableAsyncSchedulingByDefaultInReactDOM = false;
17
export const enableReactFragment = false;
18
export const enableCreateRoot = false;
19
+export const enableUserTimingAPI = __DEV__;
20
21
// React Native CS uses persistent reconciler.
22
export const enableMutatingReconciler = false;
23
export const enableNoopReconciler = false;
24
export const enablePersistentReconciler = true;
25
26
+// Only used in www builds.
27
+export function addUserTimingListener() {
28
+ invariant(false, 'Not implemented.');
29
+}
30
+
31
// Flow magic to verify the exports of this file match the original version.
32
// eslint-disable-next-line no-unused-vars
33
type Check<_X, Y: _X, X: Y=_X> = null;
packages/react-dom/src/client/ReactDOMFB.js
+3
@@ -13,6 +13,7 @@ import * as ReactInstanceMap from 'shared/ReactInstanceMap';
13
import * as ReactFiberErrorLogger
14
from 'react-reconciler/src/ReactFiberErrorLogger';
15
import ReactErrorUtils from 'shared/ReactErrorUtils';
16
+import {addUserTimingListener} from 'shared/ReactFeatureFlags';
17
18
import ReactDOM from './ReactDOM';
19
import * as ReactBrowserEventEmitter from '../events/ReactBrowserEventEmitter';
@@ -31,6 +32,8 @@ Object.assign(
32
ReactInstanceMap,
33
// Used by www msite:
34
TapEventPlugin,
35
+ // Perf experiment
36
+ addUserTimingListener,
37
},
38
);
39
packages/react-reconciler/src/ReactDebugFiberPerf.js
+388
-366
@@ -9,6 +9,7 @@
9
10
import type {Fiber} from './ReactFiber';
11
12
+import {enableUserTimingAPI} from 'shared/ReactFeatureFlags';
13
import getComponentName from 'shared/getComponentName';
14
import {
15
HostRoot,
@@ -19,9 +20,6 @@ import {
20
Fragment,
21
} from 'shared/ReactTypeOfWork';
22
22
-// Trust the developer to only use this with a __DEV__ check
23
-let ReactDebugFiberPerf = (({}: any): typeof ReactDebugFiberPerf);
24
-
23
type MeasurementPhase =
24
| 'componentWillMount'
25
| 'componentWillUnmount'
@@ -32,382 +30,406 @@ type MeasurementPhase =
30
| 'componentDidMount'
31
| 'getChildContext';
32
35
-if (__DEV__) {
36
- // Prefix measurements so that it's possible to filter them.
37
- // Longer prefixes are hard to read in DevTools.
38
- const reactEmoji = '\u269B';
39
- const warningEmoji = '\u26D4';
40
- const supportsUserTiming =
41
- typeof performance !== 'undefined' &&
42
- typeof performance.mark === 'function' &&
43
- typeof performance.clearMarks === 'function' &&
44
- typeof performance.measure === 'function' &&
45
- typeof performance.clearMeasures === 'function';
46
-
47
- // Keep track of current fiber so that we know the path to unwind on pause.
48
- // TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them?
49
- let currentFiber: Fiber | null = null;
50
- // If we're in the middle of user code, which fiber and method is it?
51
- // Reusing `currentFiber` would be confusing for this because user code fiber
52
- // can change during commit phase too, but we don't need to unwind it (since
53
- // lifecycles in the commit phase don't resemble a tree).
54
- let currentPhase: MeasurementPhase | null = null;
55
- let currentPhaseFiber: Fiber | null = null;
56
- // Did lifecycle hook schedule an update? This is often a performance problem,
57
- // so we will keep track of it, and include it in the report.
58
- // Track commits caused by cascading updates.
59
- let isCommitting: boolean = false;
60
- let hasScheduledUpdateInCurrentCommit: boolean = false;
61
- let hasScheduledUpdateInCurrentPhase: boolean = false;
62
- let commitCountInCurrentWorkLoop: number = 0;
63
- let effectCountInCurrentCommit: number = 0;
64
- // During commits, we only show a measurement once per method name
65
- // to avoid stretch the commit phase with measurement overhead.
66
- const labelsInCurrentCommit: Set<string> = new Set();
67
-
68
- const formatMarkName = (markName: string) => {
69
- return `${reactEmoji} ${markName}`;
70
- };
71
-
72
- const formatLabel = (label: string, warning: string | null) => {
73
- const prefix = warning ? `${warningEmoji} ` : `${reactEmoji} `;
74
- const suffix = warning ? ` Warning: ${warning}` : '';
75
- return `${prefix}${label}${suffix}`;
76
- };
77
-
78
- const beginMark = (markName: string) => {
79
- performance.mark(formatMarkName(markName));
80
- };
81
-
82
- const clearMark = (markName: string) => {
83
- performance.clearMarks(formatMarkName(markName));
84
- };
85
-
86
- const endMark = (label: string, markName: string, warning: string | null) => {
87
- const formattedMarkName = formatMarkName(markName);
88
- const formattedLabel = formatLabel(label, warning);
89
- try {
90
- performance.measure(formattedLabel, formattedMarkName);
91
- } catch (err) {
92
- // If previous mark was missing for some reason, this will throw.
93
- // This could only happen if React crashed in an unexpected place earlier.
94
- // Don't pile on with more errors.
33
+// Prefix measurements so that it's possible to filter them.
34
+// Longer prefixes are hard to read in DevTools.
35
+const reactEmoji = '\u269B';
36
+const warningEmoji = '\u26D4';
37
+const supportsUserTiming =
38
+ typeof performance !== 'undefined' &&
39
+ typeof performance.mark === 'function' &&
40
+ typeof performance.clearMarks === 'function' &&
41
+ typeof performance.measure === 'function' &&
42
+ typeof performance.clearMeasures === 'function';
43
+
44
+// Keep track of current fiber so that we know the path to unwind on pause.
45
+// TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them?
46
+let currentFiber: Fiber | null = null;
47
+// If we're in the middle of user code, which fiber and method is it?
48
+// Reusing `currentFiber` would be confusing for this because user code fiber
49
+// can change during commit phase too, but we don't need to unwind it (since
50
+// lifecycles in the commit phase don't resemble a tree).
51
+let currentPhase: MeasurementPhase | null = null;
52
+let currentPhaseFiber: Fiber | null = null;
53
+// Did lifecycle hook schedule an update? This is often a performance problem,
54
+// so we will keep track of it, and include it in the report.
55
+// Track commits caused by cascading updates.
56
+let isCommitting: boolean = false;
57
+let hasScheduledUpdateInCurrentCommit: boolean = false;
58
+let hasScheduledUpdateInCurrentPhase: boolean = false;
59
+let commitCountInCurrentWorkLoop: number = 0;
60
+let effectCountInCurrentCommit: number = 0;
61
+// During commits, we only show a measurement once per method name
62
+// to avoid stretch the commit phase with measurement overhead.
63
+const labelsInCurrentCommit: Set<string> = new Set();
64
+
65
+const formatMarkName = (markName: string) => {
66
+ return `${reactEmoji} ${markName}`;
67
+};
68
+
69
+const formatLabel = (label: string, warning: string | null) => {
70
+ const prefix = warning ? `${warningEmoji} ` : `${reactEmoji} `;
71
+ const suffix = warning ? ` Warning: ${warning}` : '';
72
+ return `${prefix}${label}${suffix}`;
73
+};
74
+
75
+const beginMark = (markName: string) => {
76
+ performance.mark(formatMarkName(markName));
77
+};
78
+
79
+const clearMark = (markName: string) => {
80
+ performance.clearMarks(formatMarkName(markName));
81
+};
82
+
83
+const endMark = (label: string, markName: string, warning: string | null) => {
84
+ const formattedMarkName = formatMarkName(markName);
85
+ const formattedLabel = formatLabel(label, warning);
86
+ try {
87
+ performance.measure(formattedLabel, formattedMarkName);
88
+ } catch (err) {
89
+ // If previous mark was missing for some reason, this will throw.
90
+ // This could only happen if React crashed in an unexpected place earlier.
91
+ // Don't pile on with more errors.
92
+ }
93
+ // Clear marks immediately to avoid growing buffer.
94
+ performance.clearMarks(formattedMarkName);
95
+ performance.clearMeasures(formattedLabel);
96
+};
97
+
98
+const getFiberMarkName = (label: string, debugID: number) => {
99
+ return `${label} (#${debugID})`;
100
+};
101
+
102
+const getFiberLabel = (
103
+ componentName: string,
104
+ isMounted: boolean,
105
+ phase: MeasurementPhase | null,
106
+) => {
107
+ if (phase === null) {
108
+ // These are composite component total time measurements.
109
+ return `${componentName} [${isMounted ? 'update' : 'mount'}]`;
110
+ } else {
111
+ // Composite component methods.
112
+ return `${componentName}.${phase}`;
113
+ }
114
+};
115
+
116
+const beginFiberMark = (
117
+ fiber: Fiber,
118
+ phase: MeasurementPhase | null,
119
+): boolean => {
120
+ const componentName = getComponentName(fiber) || 'Unknown';
121
+ const debugID = ((fiber._debugID: any): number);
122
+ const isMounted = fiber.alternate !== null;
123
+ const label = getFiberLabel(componentName, isMounted, phase);
124
+
125
+ if (isCommitting && labelsInCurrentCommit.has(label)) {
126
+ // During the commit phase, we don't show duplicate labels because
127
+ // there is a fixed overhead for every measurement, and we don't
128
+ // want to stretch the commit phase beyond necessary.
129
+ return false;
130
+ }
131
+ labelsInCurrentCommit.add(label);
132
+
133
+ const markName = getFiberMarkName(label, debugID);
134
+ beginMark(markName);
135
+ return true;
136
+};
137
+
138
+const clearFiberMark = (fiber: Fiber, phase: MeasurementPhase | null) => {
139
+ const componentName = getComponentName(fiber) || 'Unknown';
140
+ const debugID = ((fiber._debugID: any): number);
141
+ const isMounted = fiber.alternate !== null;
142
+ const label = getFiberLabel(componentName, isMounted, phase);
143
+ const markName = getFiberMarkName(label, debugID);
144
+ clearMark(markName);
145
+};
146
+
147
+const endFiberMark = (
148
+ fiber: Fiber,
149
+ phase: MeasurementPhase | null,
150
+ warning: string | null,
151
+) => {
152
+ const componentName = getComponentName(fiber) || 'Unknown';
153
+ const debugID = ((fiber._debugID: any): number);
154
+ const isMounted = fiber.alternate !== null;
155
+ const label = getFiberLabel(componentName, isMounted, phase);
156
+ const markName = getFiberMarkName(label, debugID);
157
+ endMark(label, markName, warning);
158
+};
159
+
160
+const shouldIgnoreFiber = (fiber: Fiber): boolean => {
161
+ // Host components should be skipped in the timeline.
162
+ // We could check typeof fiber.type, but does this work with RN?
163
+ switch (fiber.tag) {
164
+ case HostRoot:
165
+ case HostComponent:
166
+ case HostText:
167
+ case HostPortal:
168
+ case ReturnComponent:
169
+ case Fragment:
170
+ return true;
171
+ default:
172
+ return false;
173
+ }
174
+};
175
+
176
+const clearPendingPhaseMeasurement = () => {
177
+ if (currentPhase !== null && currentPhaseFiber !== null) {
178
+ clearFiberMark(currentPhaseFiber, currentPhase);
179
+ }
180
+ currentPhaseFiber = null;
181
+ currentPhase = null;
182
+ hasScheduledUpdateInCurrentPhase = false;
183
+};
184
+
185
+const pauseTimers = () => {
186
+ // Stops all currently active measurements so that they can be resumed
187
+ // if we continue in a later deferred loop from the same unit of work.
188
+ let fiber = currentFiber;
189
+ while (fiber) {
190
+ if (fiber._debugIsCurrentlyTiming) {
191
+ endFiberMark(fiber, null, null);
192
}
96
- // Clear marks immediately to avoid growing buffer.
97
- performance.clearMarks(formattedMarkName);
98
- performance.clearMeasures(formattedLabel);
99
- };
100
-
101
- const getFiberMarkName = (label: string, debugID: number) => {
102
- return `${label} (#${debugID})`;
103
- };
104
-
105
- const getFiberLabel = (
106
- componentName: string,
107
- isMounted: boolean,
108
- phase: MeasurementPhase | null,
109
- ) => {
110
- if (phase === null) {
111
- // These are composite component total time measurements.
112
- return `${componentName} [${isMounted ? 'update' : 'mount'}]`;
113
- } else {
114
- // Composite component methods.
115
- return `${componentName}.${phase}`;
193
+ fiber = fiber.return;
194
+ }
195
+};
196
+
197
+const resumeTimersRecursively = (fiber: Fiber) => {
198
+ if (fiber.return !== null) {
199
+ resumeTimersRecursively(fiber.return);
200
+ }
201
+ if (fiber._debugIsCurrentlyTiming) {
202
+ beginFiberMark(fiber, null);
203
+ }
204
+};
205
+
206
+const resumeTimers = () => {
207
+ // Resumes all measurements that were active during the last deferred loop.
208
+ if (currentFiber !== null) {
209
+ resumeTimersRecursively(currentFiber);
210
+ }
211
+};
212
+
213
+export function recordEffect(): void {
214
+ if (enableUserTimingAPI) {
215
+ effectCountInCurrentCommit++;
216
+ }
217
+}
218
+
219
+export function recordScheduleUpdate(): void {
220
+ if (enableUserTimingAPI) {
221
+ if (isCommitting) {
222
+ hasScheduledUpdateInCurrentCommit = true;
223
}
117
- };
118
-
119
- const beginFiberMark = (
120
- fiber: Fiber,
121
- phase: MeasurementPhase | null,
122
- ): boolean => {
123
- const componentName = getComponentName(fiber) || 'Unknown';
124
- const debugID = ((fiber._debugID: any): number);
125
- const isMounted = fiber.alternate !== null;
126
- const label = getFiberLabel(componentName, isMounted, phase);
127
-
128
- if (isCommitting && labelsInCurrentCommit.has(label)) {
129
- // During the commit phase, we don't show duplicate labels because
130
- // there is a fixed overhead for every measurement, and we don't
131
- // want to stretch the commit phase beyond necessary.
132
- return false;
224
+ if (
225
+ currentPhase !== null &&
226
+ currentPhase !== 'componentWillMount' &&
227
+ currentPhase !== 'componentWillReceiveProps'
228
+ ) {
229
+ hasScheduledUpdateInCurrentPhase = true;
230
}
134
- labelsInCurrentCommit.add(label);
135
-
136
- const markName = getFiberMarkName(label, debugID);
137
- beginMark(markName);
138
- return true;
139
- };
140
-
141
- const clearFiberMark = (fiber: Fiber, phase: MeasurementPhase | null) => {
142
- const componentName = getComponentName(fiber) || 'Unknown';
143
- const debugID = ((fiber._debugID: any): number);
144
- const isMounted = fiber.alternate !== null;
145
- const label = getFiberLabel(componentName, isMounted, phase);
146
- const markName = getFiberMarkName(label, debugID);
147
- clearMark(markName);
148
- };
149
-
150
- const endFiberMark = (
151
- fiber: Fiber,
152
- phase: MeasurementPhase | null,
153
- warning: string | null,
154
- ) => {
155
- const componentName = getComponentName(fiber) || 'Unknown';
156
- const debugID = ((fiber._debugID: any): number);
157
- const isMounted = fiber.alternate !== null;
158
- const label = getFiberLabel(componentName, isMounted, phase);
159
- const markName = getFiberMarkName(label, debugID);
160
- endMark(label, markName, warning);
161
- };
162
-
163
- const shouldIgnoreFiber = (fiber: Fiber): boolean => {
164
- // Host components should be skipped in the timeline.
165
- // We could check typeof fiber.type, but does this work with RN?
166
- switch (fiber.tag) {
167
- case HostRoot:
168
- case HostComponent:
169
- case HostText:
170
- case HostPortal:
171
- case ReturnComponent:
172
- case Fragment:
173
- return true;
174
- default:
175
- return false;
231
+ }
232
+}
233
+
234
+export function startWorkTimer(fiber: Fiber): void {
235
+ if (enableUserTimingAPI) {
236
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
237
+ return;
238
}
177
- };
239
+ // If we pause, this is the fiber to unwind from.
240
+ currentFiber = fiber;
241
+ if (!beginFiberMark(fiber, null)) {
242
+ return;
243
+ }
244
+ fiber._debugIsCurrentlyTiming = true;
245
+ }
246
+}
247
+
248
+export function cancelWorkTimer(fiber: Fiber): void {
249
+ if (enableUserTimingAPI) {
250
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
251
+ return;
252
+ }
253
+ // Remember we shouldn't complete measurement for this fiber.
254
+ // Otherwise flamechart will be deep even for small updates.
255
+ fiber._debugIsCurrentlyTiming = false;
256
+ clearFiberMark(fiber, null);
257
+ }
258
+}
259
179
- const clearPendingPhaseMeasurement = () => {
260
+export function stopWorkTimer(fiber: Fiber): void {
261
+ if (enableUserTimingAPI) {
262
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
263
+ return;
264
+ }
265
+ // If we pause, its parent is the fiber to unwind from.
266
+ currentFiber = fiber.return;
267
+ if (!fiber._debugIsCurrentlyTiming) {
268
+ return;
269
+ }
270
+ fiber._debugIsCurrentlyTiming = false;
271
+ endFiberMark(fiber, null, null);
272
+ }
273
+}
274
+
275
+export function stopFailedWorkTimer(fiber: Fiber): void {
276
+ if (enableUserTimingAPI) {
277
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
278
+ return;
279
+ }
280
+ // If we pause, its parent is the fiber to unwind from.
281
+ currentFiber = fiber.return;
282
+ if (!fiber._debugIsCurrentlyTiming) {
283
+ return;
284
+ }
285
+ fiber._debugIsCurrentlyTiming = false;
286
+ const warning = 'An error was thrown inside this error boundary';
287
+ endFiberMark(fiber, null, warning);
288
+ }
289
+}
290
+
291
+export function startPhaseTimer(fiber: Fiber, phase: MeasurementPhase): void {
292
+ if (enableUserTimingAPI) {
293
+ if (!supportsUserTiming) {
294
+ return;
295
+ }
296
+ clearPendingPhaseMeasurement();
297
+ if (!beginFiberMark(fiber, phase)) {
298
+ return;
299
+ }
300
+ currentPhaseFiber = fiber;
301
+ currentPhase = phase;
302
+ }
303
+}
304
+
305
+export function stopPhaseTimer(): void {
306
+ if (enableUserTimingAPI) {
307
+ if (!supportsUserTiming) {
308
+ return;
309
+ }
310
if (currentPhase !== null && currentPhaseFiber !== null) {
181
- clearFiberMark(currentPhaseFiber, currentPhase);
311
+ const warning = hasScheduledUpdateInCurrentPhase
312
+ ? 'Scheduled a cascading update'
313
+ : null;
314
+ endFiberMark(currentPhaseFiber, currentPhase, warning);
315
}
183
- currentPhaseFiber = null;
316
currentPhase = null;
185
- hasScheduledUpdateInCurrentPhase = false;
186
- };
187
-
188
- const pauseTimers = () => {
189
- // Stops all currently active measurements so that they can be resumed
190
- // if we continue in a later deferred loop from the same unit of work.
191
- let fiber = currentFiber;
192
- while (fiber) {
193
- if (fiber._debugIsCurrentlyTiming) {
194
- endFiberMark(fiber, null, null);
195
- }
196
- fiber = fiber.return;
317
+ currentPhaseFiber = null;
318
+ }
319
+}
320
+
321
+export function startWorkLoopTimer(): void {
322
+ if (enableUserTimingAPI) {
323
+ if (!supportsUserTiming) {
324
+ return;
325
}
198
- };
326
+ commitCountInCurrentWorkLoop = 0;
327
+ // This is top level call.
328
+ // Any other measurements are performed within.
329
+ beginMark('(React Tree Reconciliation)');
330
+ // Resume any measurements that were in progress during the last loop.
331
+ resumeTimers();
332
+ }
333
+}
334
200
- const resumeTimersRecursively = (fiber: Fiber) => {
201
- if (fiber.return !== null) {
202
- resumeTimersRecursively(fiber.return);
335
+export function stopWorkLoopTimer(): void {
336
+ if (enableUserTimingAPI) {
337
+ if (!supportsUserTiming) {
338
+ return;
339
}
204
- if (fiber._debugIsCurrentlyTiming) {
205
- beginFiberMark(fiber, null);
340
+ const warning = commitCountInCurrentWorkLoop > 1
341
+ ? 'There were cascading updates'
342
+ : null;
343
+ commitCountInCurrentWorkLoop = 0;
344
+ // Pause any measurements until the next loop.
345
+ pauseTimers();
346
+ endMark(
347
+ '(React Tree Reconciliation)',
348
+ '(React Tree Reconciliation)',
349
+ warning,
350
+ );
351
+ }
352
+}
353
+
354
+export function startCommitTimer(): void {
355
+ if (enableUserTimingAPI) {
356
+ if (!supportsUserTiming) {
357
+ return;
358
}
207
- };
359
+ isCommitting = true;
360
+ hasScheduledUpdateInCurrentCommit = false;
361
+ labelsInCurrentCommit.clear();
362
+ beginMark('(Committing Changes)');
363
+ }
364
+}
365
209
- const resumeTimers = () => {
210
- // Resumes all measurements that were active during the last deferred loop.
211
- if (currentFiber !== null) {
212
- resumeTimersRecursively(currentFiber);
366
+export function stopCommitTimer(): void {
367
+ if (enableUserTimingAPI) {
368
+ if (!supportsUserTiming) {
369
+ return;
370
}
214
- };
215
-
216
- ReactDebugFiberPerf = {
217
- recordEffect(): void {
218
- effectCountInCurrentCommit++;
219
- },
220
-
221
- recordScheduleUpdate(): void {
222
- if (isCommitting) {
223
- hasScheduledUpdateInCurrentCommit = true;
224
- }
225
- if (
226
- currentPhase !== null &&
227
- currentPhase !== 'componentWillMount' &&
228
- currentPhase !== 'componentWillReceiveProps'
229
- ) {
230
- hasScheduledUpdateInCurrentPhase = true;
231
- }
232
- },
233
-
234
- startWorkTimer(fiber: Fiber): void {
235
- if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
236
- return;
237
- }
238
- // If we pause, this is the fiber to unwind from.
239
- currentFiber = fiber;
240
- if (!beginFiberMark(fiber, null)) {
241
- return;
242
- }
243
- fiber._debugIsCurrentlyTiming = true;
244
- },
245
-
246
- cancelWorkTimer(fiber: Fiber): void {
247
- if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
248
- return;
249
- }
250
- // Remember we shouldn't complete measurement for this fiber.
251
- // Otherwise flamechart will be deep even for small updates.
252
- fiber._debugIsCurrentlyTiming = false;
253
- clearFiberMark(fiber, null);
254
- },
255
-
256
- stopWorkTimer(fiber: Fiber): void {
257
- if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
258
- return;
259
- }
260
- // If we pause, its parent is the fiber to unwind from.
261
- currentFiber = fiber.return;
262
- if (!fiber._debugIsCurrentlyTiming) {
263
- return;
264
- }
265
- fiber._debugIsCurrentlyTiming = false;
266
- endFiberMark(fiber, null, null);
267
- },
268
-
269
- stopFailedWorkTimer(fiber: Fiber): void {
270
- if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
271
- return;
272
- }
273
- // If we pause, its parent is the fiber to unwind from.
274
- currentFiber = fiber.return;
275
- if (!fiber._debugIsCurrentlyTiming) {
276
- return;
277
- }
278
- fiber._debugIsCurrentlyTiming = false;
279
- const warning = 'An error was thrown inside this error boundary';
280
- endFiberMark(fiber, null, warning);
281
- },
282
-
283
- startPhaseTimer(fiber: Fiber, phase: MeasurementPhase): void {
284
- if (!supportsUserTiming) {
285
- return;
286
- }
287
- clearPendingPhaseMeasurement();
288
- if (!beginFiberMark(fiber, phase)) {
289
- return;
290
- }
291
- currentPhaseFiber = fiber;
292
- currentPhase = phase;
293
- },
294
-
295
- stopPhaseTimer(): void {
296
- if (!supportsUserTiming) {
297
- return;
298
- }
299
- if (currentPhase !== null && currentPhaseFiber !== null) {
300
- const warning = hasScheduledUpdateInCurrentPhase
301
- ? 'Scheduled a cascading update'
302
- : null;
303
- endFiberMark(currentPhaseFiber, currentPhase, warning);
304
- }
305
- currentPhase = null;
306
- currentPhaseFiber = null;
307
- },
308
-
309
- startWorkLoopTimer(): void {
310
- if (!supportsUserTiming) {
311
- return;
312
- }
313
- commitCountInCurrentWorkLoop = 0;
314
- // This is top level call.
315
- // Any other measurements are performed within.
316
- beginMark('(React Tree Reconciliation)');
317
- // Resume any measurements that were in progress during the last loop.
318
- resumeTimers();
319
- },
320
-
321
- stopWorkLoopTimer(): void {
322
- if (!supportsUserTiming) {
323
- return;
324
- }
325
- const warning = commitCountInCurrentWorkLoop > 1
326
- ? 'There were cascading updates'
327
- : null;
328
- commitCountInCurrentWorkLoop = 0;
329
- // Pause any measurements until the next loop.
330
- pauseTimers();
331
- endMark(
332
- '(React Tree Reconciliation)',
333
- '(React Tree Reconciliation)',
334
- warning,
335
- );
336
- },
337
-
338
- startCommitTimer(): void {
339
- if (!supportsUserTiming) {
340
- return;
341
- }
342
- isCommitting = true;
343
- hasScheduledUpdateInCurrentCommit = false;
344
- labelsInCurrentCommit.clear();
345
- beginMark('(Committing Changes)');
346
- },
347
-
348
- stopCommitTimer(): void {
349
- if (!supportsUserTiming) {
350
- return;
351
- }
352
-
353
- let warning = null;
354
- if (hasScheduledUpdateInCurrentCommit) {
355
- warning = 'Lifecycle hook scheduled a cascading update';
356
- } else if (commitCountInCurrentWorkLoop > 0) {
357
- warning = 'Caused by a cascading update in earlier commit';
358
- }
359
- hasScheduledUpdateInCurrentCommit = false;
360
- commitCountInCurrentWorkLoop++;
361
- isCommitting = false;
362
- labelsInCurrentCommit.clear();
363
-
364
- endMark('(Committing Changes)', '(Committing Changes)', warning);
365
- },
366
-
367
- startCommitHostEffectsTimer(): void {
368
- if (!supportsUserTiming) {
369
- return;
370
- }
371
- effectCountInCurrentCommit = 0;
372
- beginMark('(Committing Host Effects)');
373
- },
374
-
375
- stopCommitHostEffectsTimer(): void {
376
- if (!supportsUserTiming) {
377
- return;
378
- }
379
- const count = effectCountInCurrentCommit;
380
- effectCountInCurrentCommit = 0;
381
- endMark(
382
- `(Committing Host Effects: ${count} Total)`,
383
- '(Committing Host Effects)',
384
- null,
385
- );
386
- },
387
-
388
- startCommitLifeCyclesTimer(): void {
389
- if (!supportsUserTiming) {
390
- return;
391
- }
392
- effectCountInCurrentCommit = 0;
393
- beginMark('(Calling Lifecycle Methods)');
394
- },
395
-
396
- stopCommitLifeCyclesTimer(): void {
397
- if (!supportsUserTiming) {
398
- return;
399
- }
400
- const count = effectCountInCurrentCommit;
401
- effectCountInCurrentCommit = 0;
402
- endMark(
403
- `(Calling Lifecycle Methods: ${count} Total)`,
404
- '(Calling Lifecycle Methods)',
405
- null,
406
- );
407
- },
408
- };
371
+
372
+ let warning = null;
373
+ if (hasScheduledUpdateInCurrentCommit) {
374
+ warning = 'Lifecycle hook scheduled a cascading update';
375
+ } else if (commitCountInCurrentWorkLoop > 0) {
376
+ warning = 'Caused by a cascading update in earlier commit';
377
+ }
378
+ hasScheduledUpdateInCurrentCommit = false;
379
+ commitCountInCurrentWorkLoop++;
380
+ isCommitting = false;
381
+ labelsInCurrentCommit.clear();
382
+
383
+ endMark('(Committing Changes)', '(Committing Changes)', warning);
384
+ }
385
}
386
411
-// TODO: convert to named exports
412
-// if this doesn't inflate the bundle.
413
-export default ReactDebugFiberPerf;
387
+export function startCommitHostEffectsTimer(): void {
388
+ if (enableUserTimingAPI) {
389
+ if (!supportsUserTiming) {
390
+ return;
391
+ }
392
+ effectCountInCurrentCommit = 0;
393
+ beginMark('(Committing Host Effects)');
394
+ }
395
+}
396
+
397
+export function stopCommitHostEffectsTimer(): void {
398
+ if (enableUserTimingAPI) {
399
+ if (!supportsUserTiming) {
400
+ return;
401
+ }
402
+ const count = effectCountInCurrentCommit;
403
+ effectCountInCurrentCommit = 0;
404
+ endMark(
405
+ `(Committing Host Effects: ${count} Total)`,
406
+ '(Committing Host Effects)',
407
+ null,
408
+ );
409
+ }
410
+}
411
+
412
+export function startCommitLifeCyclesTimer(): void {
413
+ if (enableUserTimingAPI) {
414
+ if (!supportsUserTiming) {
415
+ return;
416
+ }
417
+ effectCountInCurrentCommit = 0;
418
+ beginMark('(Calling Lifecycle Methods)');
419
+ }
420
+}
421
+
422
+export function stopCommitLifeCyclesTimer(): void {
423
+ if (enableUserTimingAPI) {
424
+ if (!supportsUserTiming) {
425
+ return;
426
+ }
427
+ const count = effectCountInCurrentCommit;
428
+ effectCountInCurrentCommit = 0;
429
+ endMark(
430
+ `(Calling Lifecycle Methods: ${count} Total)`,
431
+ '(Calling Lifecycle Methods)',
432
+ null,
433
+ );
434
+ }
435
+}
packages/react-reconciler/src/ReactFiberBeginWork.js
+3
-8
@@ -40,7 +40,7 @@ import invariant from 'fbjs/lib/invariant';
40
import getComponentName from 'shared/getComponentName';
41
import warning from 'fbjs/lib/warning';
42
import ReactDebugCurrentFiber from './ReactDebugCurrentFiber';
43
-import ReactDebugFiberPerf from './ReactDebugFiberPerf';
43
+import {cancelWorkTimer} from './ReactDebugFiberPerf';
44
45
import ReactFiberClassComponent from './ReactFiberClassComponent';
46
import {
@@ -60,7 +60,6 @@ import {
60
} from './ReactFiberContext';
61
import {NoWork, Never} from './ReactFiberExpirationTime';
62
63
-var {cancelWorkTimer} = ReactDebugFiberPerf;
63
if (__DEV__) {
64
var warnedAboutStatelessRefs = {};
65
}
@@ -666,9 +665,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
665
current,
666
workInProgress: Fiber,
667
): Fiber | null {
669
- if (__DEV__) {
670
- cancelWorkTimer(workInProgress);
671
- }
668
+ cancelWorkTimer(workInProgress);
669
670
// TODO: We should ideally be able to bail out early if the children have no
671
// more work to do. However, since we don't have a separation of this
@@ -689,9 +686,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
686
}
687
688
function bailoutOnLowPriority(current, workInProgress) {
692
- if (__DEV__) {
693
- cancelWorkTimer(workInProgress);
694
- }
689
+ cancelWorkTimer(workInProgress);
690
691
// TODO: Handle HostComponent tags here as well and call pushHostContext()?
692
// See PR 8590 discussion for context
packages/react-reconciler/src/ReactFiberClassComponent.js
+10
-26
@@ -20,7 +20,7 @@ import shallowEqual from 'fbjs/lib/shallowEqual';
20
import invariant from 'fbjs/lib/invariant';
21
import warning from 'fbjs/lib/warning';
22
23
-import ReactDebugFiberPerf from './ReactDebugFiberPerf';
23
+import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
24
import {AsyncUpdates} from './ReactTypeOfInternalContext';
25
import {
26
cacheContext,
@@ -34,7 +34,6 @@ import {
34
} from './ReactFiberUpdateQueue';
35
import {hasContextChanged} from './ReactFiberContext';
36
37
-var {startPhaseTimer, stopPhaseTimer} = ReactDebugFiberPerf;
37
const fakeInternalInstance = {};
38
const isArray = Array.isArray;
39
@@ -161,17 +160,13 @@ export default function(
160
const instance = workInProgress.stateNode;
161
const type = workInProgress.type;
162
if (typeof instance.shouldComponentUpdate === 'function') {
164
- if (__DEV__) {
165
- startPhaseTimer(workInProgress, 'shouldComponentUpdate');
166
- }
163
+ startPhaseTimer(workInProgress, 'shouldComponentUpdate');
164
const shouldUpdate = instance.shouldComponentUpdate(
165
newProps,
166
newState,
167
newContext,
168
);
172
- if (__DEV__) {
173
- stopPhaseTimer();
174
- }
169
+ stopPhaseTimer();
170
171
if (__DEV__) {
172
warning(
@@ -365,14 +360,11 @@ export default function(
360
}
361
362
function callComponentWillMount(workInProgress, instance) {
368
- if (__DEV__) {
369
- startPhaseTimer(workInProgress, 'componentWillMount');
370
- }
363
+ startPhaseTimer(workInProgress, 'componentWillMount');
364
const oldState = instance.state;
365
instance.componentWillMount();
373
- if (__DEV__) {
374
- stopPhaseTimer();
375
- }
366
+
367
+ stopPhaseTimer();
368
369
if (oldState !== instance.state) {
370
if (__DEV__) {
@@ -394,14 +386,10 @@ export default function(
386
newProps,
387
newContext,
388
) {
397
- if (__DEV__) {
398
- startPhaseTimer(workInProgress, 'componentWillReceiveProps');
399
- }
389
+ startPhaseTimer(workInProgress, 'componentWillReceiveProps');
390
const oldState = instance.state;
391
instance.componentWillReceiveProps(newProps, newContext);
402
- if (__DEV__) {
403
- stopPhaseTimer();
404
- }
392
+ stopPhaseTimer();
393
394
if (instance.state !== oldState) {
395
if (__DEV__) {
@@ -673,13 +661,9 @@ export default function(
661
662
if (shouldUpdate) {
663
if (typeof instance.componentWillUpdate === 'function') {
676
- if (__DEV__) {
677
- startPhaseTimer(workInProgress, 'componentWillUpdate');
678
- }
664
+ startPhaseTimer(workInProgress, 'componentWillUpdate');
665
instance.componentWillUpdate(newProps, newState, newContext);
680
- if (__DEV__) {
681
- stopPhaseTimer();
682
- }
666
+ stopPhaseTimer();
667
}
668
if (typeof instance.componentDidUpdate === 'function') {
669
workInProgress.effectTag |= Update;
packages/react-reconciler/src/ReactFiberCommitWork.js
+14
-27
@@ -29,10 +29,9 @@ import invariant from 'fbjs/lib/invariant';
29
30
import {commitCallbacks} from './ReactFiberUpdateQueue';
31
import {onCommitUnmount} from './ReactFiberDevToolsHook';
32
-import ReactDebugFiberPerf from './ReactDebugFiberPerf';
32
+import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
33
34
var {invokeGuardedCallback, hasCaughtError, clearCaughtError} = ReactErrorUtils;
35
-var {startPhaseTimer, stopPhaseTimer} = ReactDebugFiberPerf;
35
36
export default function<T, P, I, TI, PI, C, CC, CX, PL>(
37
config: HostConfig<T, P, I, TI, PI, C, CC, CX, PL>,
@@ -40,22 +39,20 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
39
) {
40
const {getPublicInstance, mutation, persistence} = config;
41
43
- if (__DEV__) {
44
- var callComponentWillUnmountWithTimerInDev = function(current, instance) {
45
- startPhaseTimer(current, 'componentWillUnmount');
46
- instance.props = current.memoizedProps;
47
- instance.state = current.memoizedState;
48
- instance.componentWillUnmount();
49
- stopPhaseTimer();
50
- };
51
- }
42
+ var callComponentWillUnmountWithTimer = function(current, instance) {
43
+ startPhaseTimer(current, 'componentWillUnmount');
44
+ instance.props = current.memoizedProps;
45
+ instance.state = current.memoizedState;
46
+ instance.componentWillUnmount();
47
+ stopPhaseTimer();
48
+ };
49
50
// Capture errors so they don't interrupt unmounting.
51
function safelyCallComponentWillUnmount(current, instance) {
52
if (__DEV__) {
53
invokeGuardedCallback(
54
null,
58
- callComponentWillUnmountWithTimerInDev,
55
+ callComponentWillUnmountWithTimer,
56
null,
57
current,
58
instance,
@@ -66,9 +63,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
63
}
64
} else {
65
try {
69
- instance.props = current.memoizedProps;
70
- instance.state = current.memoizedState;
71
- instance.componentWillUnmount();
66
+ callComponentWillUnmountWithTimer(current, instance);
67
} catch (unmountError) {
68
captureError(current, unmountError);
69
}
@@ -100,27 +95,19 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
95
const instance = finishedWork.stateNode;
96
if (finishedWork.effectTag & Update) {
97
if (current === null) {
103
- if (__DEV__) {
104
- startPhaseTimer(finishedWork, 'componentDidMount');
105
- }
98
+ startPhaseTimer(finishedWork, 'componentDidMount');
99
instance.props = finishedWork.memoizedProps;
100
instance.state = finishedWork.memoizedState;
101
instance.componentDidMount();
109
- if (__DEV__) {
110
- stopPhaseTimer();
111
- }
102
+ stopPhaseTimer();
103
} else {
104
const prevProps = current.memoizedProps;
105
const prevState = current.memoizedState;
115
- if (__DEV__) {
116
- startPhaseTimer(finishedWork, 'componentDidUpdate');
117
- }
106
+ startPhaseTimer(finishedWork, 'componentDidUpdate');
107
instance.props = finishedWork.memoizedProps;
108
instance.state = finishedWork.memoizedState;
109
instance.componentDidUpdate(prevProps, prevState);
121
- if (__DEV__) {
122
- stopPhaseTimer();
123
- }
110
+ stopPhaseTimer();
111
}
112
}
113
const updateQueue = finishedWork.updateQueue;
packages/react-reconciler/src/ReactFiberContext.js
+6
-8
@@ -20,9 +20,7 @@ import checkPropTypes from 'prop-types/checkPropTypes';
20
21
import {createCursor, pop, push} from './ReactFiberStack';
22
import ReactDebugCurrentFiber from './ReactDebugCurrentFiber';
23
-import ReactDebugFiberPerf from './ReactDebugFiberPerf';
24
-
25
-const {startPhaseTimer, stopPhaseTimer} = ReactDebugFiberPerf;
23
+import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
24
25
if (__DEV__) {
26
var warnedAboutMissingGetChildContext = {};
@@ -177,12 +175,12 @@ export function processChildContext(
175
let childContext;
176
if (__DEV__) {
177
ReactDebugCurrentFiber.setCurrentPhase('getChildContext');
180
- startPhaseTimer(fiber, 'getChildContext');
181
- childContext = instance.getChildContext();
182
- stopPhaseTimer();
178
+ }
179
+ startPhaseTimer(fiber, 'getChildContext');
180
+ childContext = instance.getChildContext();
181
+ stopPhaseTimer();
182
+ if (__DEV__) {
183
ReactDebugCurrentFiber.setCurrentPhase(null);
184
- } else {
185
- childContext = instance.getChildContext();
184
}
185
for (let contextKey in childContext) {
186
invariant(
packages/react-reconciler/src/ReactFiberScheduler.js
+34
-63
@@ -46,7 +46,21 @@ import ReactFiberHostContext from './ReactFiberHostContext';
46
import ReactFiberHydrationContext from './ReactFiberHydrationContext';
47
import ReactFiberInstrumentation from './ReactFiberInstrumentation';
48
import ReactDebugCurrentFiber from './ReactDebugCurrentFiber';
49
-import ReactDebugFiberPerf from './ReactDebugFiberPerf';
49
+import {
50
+ recordEffect,
51
+ recordScheduleUpdate,
52
+ startWorkTimer,
53
+ stopWorkTimer,
54
+ stopFailedWorkTimer,
55
+ startWorkLoopTimer,
56
+ stopWorkLoopTimer,
57
+ startCommitTimer,
58
+ stopCommitTimer,
59
+ startCommitHostEffectsTimer,
60
+ stopCommitHostEffectsTimer,
61
+ startCommitLifeCyclesTimer,
62
+ stopCommitLifeCyclesTimer,
63
+} from './ReactDebugFiberPerf';
64
import {popContextProvider} from './ReactFiberContext';
65
import {reset} from './ReactFiberStack';
66
import {logCapturedError} from './ReactFiberErrorLogger';
@@ -64,21 +78,6 @@ import {getUpdateExpirationTime} from './ReactFiberUpdateQueue';
78
import {resetContext} from './ReactFiberContext';
79
80
var {invokeGuardedCallback, hasCaughtError, clearCaughtError} = ReactErrorUtils;
67
-var {
68
- recordEffect,
69
- recordScheduleUpdate,
70
- startWorkTimer,
71
- stopWorkTimer,
72
- stopFailedWorkTimer,
73
- startWorkLoopTimer,
74
- stopWorkLoopTimer,
75
- startCommitTimer,
76
- stopCommitTimer,
77
- startCommitHostEffectsTimer,
78
- stopCommitHostEffectsTimer,
79
- startCommitLifeCyclesTimer,
80
- stopCommitLifeCyclesTimer,
81
-} = ReactDebugFiberPerf;
81
82
export type CapturedError = {
83
componentName: ?string,
@@ -229,8 +228,8 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
228
while (nextEffect !== null) {
229
if (__DEV__) {
230
ReactDebugCurrentFiber.setCurrentFiber(nextEffect);
232
- recordEffect();
231
}
232
+ recordEffect();
233
234
const effectTag = nextEffect.effectTag;
235
if (effectTag & ContentReset) {
@@ -298,24 +297,18 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
297
const effectTag = nextEffect.effectTag;
298
299
if (effectTag & (Update | Callback)) {
301
- if (__DEV__) {
302
- recordEffect();
303
- }
300
+ recordEffect();
301
const current = nextEffect.alternate;
302
commitLifeCycles(current, nextEffect);
303
}
304
305
if (effectTag & Ref) {
309
- if (__DEV__) {
310
- recordEffect();
311
- }
306
+ recordEffect();
307
commitAttachRef(nextEffect);
308
}
309
310
if (effectTag & Err) {
316
- if (__DEV__) {
317
- recordEffect();
318
- }
311
+ recordEffect();
312
commitErrorHandling(nextEffect);
313
}
314
@@ -338,9 +331,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
331
// captured elsewhere, to prevent the unmount from being interrupted.
332
isWorking = true;
333
isCommitting = true;
341
- if (__DEV__) {
342
- startCommitTimer();
343
- }
334
+ startCommitTimer();
335
336
const root: FiberRoot = finishedWork.stateNode;
337
invariant(
@@ -377,9 +368,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
368
// The first pass performs all the host insertions, updates, deletions and
369
// ref unmounts.
370
nextEffect = firstEffect;
380
- if (__DEV__) {
381
- startCommitHostEffectsTimer();
382
- }
371
+ startCommitHostEffectsTimer();
372
while (nextEffect !== null) {
373
let didError = false;
374
let error;
@@ -410,9 +399,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
399
}
400
}
401
}
413
- if (__DEV__) {
414
- stopCommitHostEffectsTimer();
415
- }
402
+ stopCommitHostEffectsTimer();
403
404
resetAfterCommit();
405
@@ -427,9 +414,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
414
// and deletions in the entire tree have already been invoked.
415
// This pass also triggers any renderer-specific initial effects.
416
nextEffect = firstEffect;
430
- if (__DEV__) {
431
- startCommitLifeCyclesTimer();
432
- }
417
+ startCommitLifeCyclesTimer();
418
while (nextEffect !== null) {
419
let didError = false;
420
let error;
@@ -462,10 +447,8 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
447
448
isCommitting = false;
449
isWorking = false;
465
- if (__DEV__) {
466
- stopCommitLifeCyclesTimer();
467
- stopCommitTimer();
468
- }
450
+ stopCommitLifeCyclesTimer();
451
+ stopCommitTimer();
452
if (typeof onCommitRoot === 'function') {
453
onCommitRoot(finishedWork.stateNode);
454
}
@@ -551,9 +534,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
534
resetExpirationTime(workInProgress, nextRenderExpirationTime);
535
536
if (next !== null) {
554
- if (__DEV__) {
555
- stopWorkTimer(workInProgress);
556
- }
537
+ stopWorkTimer(workInProgress);
538
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
539
ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
540
}
@@ -595,9 +576,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
576
}
577
}
578
598
- if (__DEV__) {
599
- stopWorkTimer(workInProgress);
600
- }
579
+ stopWorkTimer(workInProgress);
580
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
581
ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
582
}
@@ -631,8 +610,8 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
610
const current = workInProgress.alternate;
611
612
// See if beginning this work spawns more work.
613
+ startWorkTimer(workInProgress);
614
if (__DEV__) {
635
- startWorkTimer(workInProgress);
615
ReactDebugCurrentFiber.setCurrentFiber(workInProgress);
616
}
617
let next = beginWork(current, workInProgress, nextRenderExpirationTime);
@@ -661,8 +640,8 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
640
const current = workInProgress.alternate;
641
642
// See if beginning this work spawns more work.
643
+ startWorkTimer(workInProgress);
644
if (__DEV__) {
665
- startWorkTimer(workInProgress);
645
ReactDebugCurrentFiber.setCurrentFiber(workInProgress);
646
}
647
let next = beginFailedWork(
@@ -773,9 +752,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
752
root: FiberRoot,
753
expirationTime: ExpirationTime,
754
): Fiber | null {
776
- if (__DEV__) {
777
- startWorkLoopTimer();
778
- }
755
+ startWorkLoopTimer();
756
757
invariant(
758
!isWorking,
@@ -892,9 +869,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
869
didFatal = false;
870
firstUncaughtError = null;
871
895
- if (__DEV__) {
896
- stopWorkLoopTimer();
897
- }
872
+ stopWorkLoopTimer();
873
874
if (uncaughtError !== null) {
875
onUncaughtError(uncaughtError);
@@ -1131,11 +1106,9 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
1106
break;
1107
}
1108
if (node === to || node.alternate === to) {
1134
- if (__DEV__) {
1135
- stopFailedWorkTimer(node);
1136
- }
1109
+ stopFailedWorkTimer(node);
1110
break;
1138
- } else if (__DEV__) {
1111
+ } else {
1112
stopWorkTimer(node);
1113
}
1114
node = node.return;
@@ -1190,9 +1163,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
1163
expirationTime: ExpirationTime,
1164
isErrorRecovery: boolean,
1165
) {
1193
- if (__DEV__) {
1194
- recordScheduleUpdate();
1195
- }
1166
+ recordScheduleUpdate();
1167
1168
if (__DEV__) {
1169
if (!isErrorRecovery && fiber.tag === ClassComponent) {
packages/shared/ReactFeatureFlags.js
+8
@@ -7,12 +7,15 @@
7
* @flow
8
*/
9
10
+import invariant from 'fbjs/lib/invariant';
11
+
12
export const enableAsyncSubtreeAPI = true;
13
export const enableAsyncSchedulingByDefaultInReactDOM = false;
14
// Exports React.Fragment
15
export const enableReactFragment = false;
16
// Exports ReactDOM.createRoot
17
export const enableCreateRoot = false;
18
+export const enableUserTimingAPI = __DEV__;
19
20
// Mutating mode (React DOM, React ART, React Native):
21
export const enableMutatingReconciler = true;
@@ -20,3 +23,8 @@ export const enableMutatingReconciler = true;
23
export const enableNoopReconciler = false;
24
// Experimental persistent mode (CS):
25
export const enablePersistentReconciler = false;
26
+
27
+// Only used in www builds.
28
+export function addUserTimingListener() {
29
+ invariant(false, 'Not implemented.');
30
+}
scripts/rollup/shims/rollup/ReactFeatureFlags-www.js
+27
@@ -16,11 +16,38 @@ export const {
16
enableAsyncSchedulingByDefaultInReactDOM,
17
enableReactFragment,
18
enableCreateRoot,
19
+ // Reconciler flags
20
enableMutatingReconciler,
21
enableNoopReconciler,
22
enablePersistentReconciler,
23
} = require('ReactFeatureFlags');
24
25
+export let enableUserTimingAPI = __DEV__;
26
+
27
+let refCount = 0;
28
+export function addUserTimingListener() {
29
+ if (__DEV__) {
30
+ // Noop.
31
+ return () => {};
32
+ }
33
+ refCount++;
34
+ updateFlagOutsideOfReactCallStack();
35
+ return () => {
36
+ refCount--;
37
+ updateFlagOutsideOfReactCallStack();
38
+ };
39
+}
40
+
41
+let timeout = null;
42
+function updateFlagOutsideOfReactCallStack() {
43
+ if (!timeout) {
44
+ timeout = setTimeout(() => {
45
+ timeout = null;
46
+ enableUserTimingAPI = refCount > 0;
47
+ });
48
+ }
49
+}
50
+
51
// Flow magic to verify the exports of this file match the original version.
52
// eslint-disable-next-line no-unused-vars
53
type Check<_X, Y: _X, X: Y=_X> = null;