@samitouri / QOS-React-2 / commits / 3b27160f82

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;