@samitouri / QOS-React / commits / b572e3c907

moved non DEV methods to a new file called ReactFiberComponentTreeHook.js

This moves the non DEV methods to a new file called ReactFiberComponentTreeHook, updates existing references to DEV functions to remain inside DEV flags so they do not pull in ReactComponentTreeHook

Dominic Gannaway committed Mar 6, 2017 at 14:19 UTC b572e3c907e31700b140d750fd77977f0fd15885
12 files changed +478 -486
src/isomorphic/classic/element/ReactDebugCurrentFrame.js
+8 -6
@@ -14,16 +14,18 @@
14
15 import type { Fiber } from 'ReactFiber';
16 import type { DebugID } from 'ReactInstanceType';
17 -import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook';
17
18 const ReactDebugCurrentFrame = {};
19
20 if (__DEV__) {
22 - const {
21 + var {
22 getStackAddendumByID,
24 - getStackAddendumByWorkInProgressFiber,
23 getCurrentStackAddendum,
26 - }: ComponentTreeHookDevType = (require('ReactComponentTreeHook'): any);
24 + } = require('ReactComponentTreeHook');
25 + var {
26 + getStackAddendumByWorkInProgressFiber,
27 + } = require('ReactFiberComponentTreeHook');
28 +
29 // Component that is being worked on
30 ReactDebugCurrentFrame.current = (null : Fiber | DebugID | null);
31
@@ -38,7 +40,7 @@ if (__DEV__) {
40 if (typeof current === 'number') {
41 // DebugID from Stack.
42 const debugID = current;
41 - stack = getStackAddendumByID && getStackAddendumByID(debugID);
43 + stack = getStackAddendumByID(debugID);
44 } else if (typeof current.tag === 'number') {
45 // This is a Fiber.
46 // The stack will only be correct if this is a work in progress
@@ -47,7 +49,7 @@ if (__DEV__) {
49 stack = getStackAddendumByWorkInProgressFiber(workInProgress);
50 }
51 } else if (element !== null) {
50 - stack = getCurrentStackAddendum && getCurrentStackAddendum(element);
52 + stack = getCurrentStackAddendum(element);
53 }
54 return stack;
55 };
src/isomorphic/classic/element/ReactElementValidator.js
+5 -3
@@ -19,7 +19,6 @@
19 'use strict';
20
21 var ReactCurrentOwner = require('ReactCurrentOwner');
22 -var ReactComponentTreeHook = require('ReactComponentTreeHook');
22 var ReactElement = require('ReactElement');
23
24 var checkReactTypeSpec = require('checkReactTypeSpec');
@@ -31,6 +30,9 @@ var getIteratorFn = require('getIteratorFn');
30 if (__DEV__) {
31 var warning = require('fbjs/lib/warning');
32 var ReactDebugCurrentFrame = require('ReactDebugCurrentFrame');
33 + var {
34 + getCurrentStackAddendum,
35 + } = require('ReactComponentTreeHook');
36 }
37
38 function getDeclarationErrorAddendum() {
@@ -122,7 +124,7 @@ function validateExplicitKey(element, parentType) {
124 '%s%s See https://fb.me/react-warning-keys for more information.%s',
125 currentComponentErrorInfo,
126 childOwner,
125 - ReactComponentTreeHook.getCurrentStackAddendum(element)
127 + getCurrentStackAddendum && getCurrentStackAddendum(element)
128 );
129 }
130
@@ -225,7 +227,7 @@ var ReactElementValidator = {
227 info += getDeclarationErrorAddendum();
228 }
229
228 - info += ReactComponentTreeHook.getCurrentStackAddendum();
230 + info += getCurrentStackAddendum && getCurrentStackAddendum();
231
232 warning(
233 false,
src/isomorphic/hooks/ReactComponentTreeHook.js
+337 -420
@@ -12,476 +12,393 @@
12
13 'use strict';
14
15 +var ReactCurrentOwner = require('ReactCurrentOwner');
16 +var {
17 + getStackAddendumByWorkInProgressFiber,
18 + describeComponentFrame,
19 +} = require('ReactFiberComponentTreeHook');
20 +var invariant = require('fbjs/lib/invariant');
21 +var warning = require('fbjs/lib/warning');
22 +var getComponentName = require('getComponentName');
23 +
24 import type { ReactElement, Source } from 'ReactElementType';
25 import type { DebugID } from 'ReactInstanceType';
26 import type { Fiber } from 'ReactFiber';
27
19 -var getComponentName = require('getComponentName');
20 -var ReactTypeOfWork = require('ReactTypeOfWork');
21 -var {
22 - IndeterminateComponent,
23 - FunctionalComponent,
24 - ClassComponent,
25 - HostComponent,
26 -} = ReactTypeOfWork;
27 -
28 -function describeComponentFrame(name, source, ownerName) {
29 - return '\n in ' + (name || 'Unknown') + (
30 - source ?
31 - ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' +
32 - source.lineNumber + ')' :
33 - ownerName ?
34 - ' (created by ' + ownerName + ')' :
35 - ''
28 +function isNative(fn) {
29 + // Based on isNative() from Lodash
30 + var funcToString = Function.prototype.toString;
31 + var hasOwnProperty = Object.prototype.hasOwnProperty;
32 + var reIsNative = RegExp('^' + funcToString
33 + // Take an example native function source for comparison
34 + .call(hasOwnProperty)
35 + // Strip regex characters so we can use it for regex
36 + .replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
37 + // Remove hasOwnProperty from the template to make it generic
38 + .replace(
39 + /hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,
40 + '$1.*?'
41 + ) + '$'
42 );
37 -}
38 -
39 -function describeFiber(fiber : Fiber) : string {
40 - switch (fiber.tag) {
41 - case IndeterminateComponent:
42 - case FunctionalComponent:
43 - case ClassComponent:
44 - case HostComponent:
45 - var owner = fiber._debugOwner;
46 - var source = fiber._debugSource;
47 - var name = getComponentName(fiber);
48 - var ownerName = null;
49 - if (owner) {
50 - ownerName = getComponentName(owner);
51 - }
52 - return describeComponentFrame(name, source, ownerName);
53 - default:
54 - return '';
43 + try {
44 + var source = funcToString.call(fn);
45 + return reIsNative.test(source);
46 + } catch (err) {
47 + return false;
48 }
49 }
50
58 -export type ComponentTreeHookType = {
59 - getStackAddendumByWorkInProgressFiber: (Fiber) => any,
60 -};
51 +var canUseCollections = (
52 + // Array.from
53 + typeof Array.from === 'function' &&
54 + // Map
55 + typeof Map === 'function' &&
56 + isNative(Map) &&
57 + // Map.prototype.keys
58 + Map.prototype != null &&
59 + typeof Map.prototype.keys === 'function' &&
60 + isNative(Map.prototype.keys) &&
61 + // Set
62 + typeof Set === 'function' &&
63 + isNative(Set) &&
64 + // Set.prototype.keys
65 + Set.prototype != null &&
66 + typeof Set.prototype.keys === 'function' &&
67 + isNative(Set.prototype.keys)
68 +);
69 +
70 +var setItem;
71 +var getItem;
72 +var removeItem;
73 +var getItemIDs;
74 +var addRoot;
75 +var removeRoot;
76 +var getRootIDs;
77 +
78 +if (canUseCollections) {
79 + var itemMap = new Map();
80 + var rootIDSet = new Set();
81 +
82 + setItem = function(id, item) {
83 + itemMap.set(id, item);
84 + };
85 + getItem = function(id) {
86 + return itemMap.get(id);
87 + };
88 + removeItem = function(id) {
89 + itemMap.delete(id);
90 + };
91 + getItemIDs = function() {
92 + return Array.from(itemMap.keys());
93 + };
94
62 -export type ComponentTreeHookDevType = {
63 - getStackAddendumByWorkInProgressFiber: (Fiber) => any,
64 - getStackAddendumByID: () => any,
65 - getCurrentStackAddendum: () => any,
66 - purgeUnmountedComponents: () => any,
67 - getOwnerID: (DebugID) => any,
68 - getParentID: (DebugID) => any,
69 - getDisplayName: (DebugID) => any,
70 - getText: (DebugID) => any,
71 - getUpdateCount: (DebugID) => any,
72 - getChildIDs: (DebugID) => any,
73 - getRegisteredIDs: () => any,
74 - getElement: () => any,
75 -};
95 + addRoot = function(id) {
96 + rootIDSet.add(id);
97 + };
98 + removeRoot = function(id) {
99 + rootIDSet.delete(id);
100 + };
101 + getRootIDs = function() {
102 + return Array.from(rootIDSet.keys());
103 + };
104
77 -var ReactComponentTreeHook: ComponentTreeHookType = {
78 - // This function can only be called with a work-in-progress fiber and
79 - // only during begin or complete phase. Do not call it under any other
80 - // circumstances.
81 - getStackAddendumByWorkInProgressFiber(workInProgress : Fiber) : string {
82 - var info = '';
83 - var node = workInProgress;
84 - do {
85 - info += describeFiber(node);
86 - // Otherwise this return pointer might point to the wrong tree:
87 - node = node.return;
88 - } while (node);
89 - return info;
90 - },
91 -};
105 +} else {
106 + var itemByKey = {};
107 + var rootByKey = {};
108
93 -if (__DEV__) {
94 - var ReactCurrentOwner = require('ReactCurrentOwner');
95 - var invariant = require('fbjs/lib/invariant');
96 - var warning = require('fbjs/lib/warning');
97 -
98 - var isNative = function(fn) {
99 - // Based on isNative() from Lodash
100 - var funcToString = Function.prototype.toString;
101 - var hasOwnProperty = Object.prototype.hasOwnProperty;
102 - var reIsNative = RegExp('^' + funcToString
103 - // Take an example native function source for comparison
104 - .call(hasOwnProperty)
105 - // Strip regex characters so we can use it for regex
106 - .replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
107 - // Remove hasOwnProperty from the template to make it generic
108 - .replace(
109 - /hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,
110 - '$1.*?'
111 - ) + '$'
112 - );
113 - try {
114 - var source = funcToString.call(fn);
115 - return reIsNative.test(source);
116 - } catch (err) {
117 - return false;
118 - }
109 + // Use non-numeric keys to prevent V8 performance issues:
110 + // https://github.com/facebook/react/pull/7232
111 + var getKeyFromID = function(id: DebugID): string {
112 + return '.' + id;
113 + };
114 + var getIDFromKey = function(key: string): DebugID {
115 + return parseInt(key.substr(1), 10);
116 };
117
121 - var canUseCollections = (
122 - // Array.from
123 - typeof Array.from === 'function' &&
124 - // Map
125 - typeof Map === 'function' &&
126 - isNative(Map) &&
127 - // Map.prototype.keys
128 - Map.prototype != null &&
129 - typeof Map.prototype.keys === 'function' &&
130 - isNative(Map.prototype.keys) &&
131 - // Set
132 - typeof Set === 'function' &&
133 - isNative(Set) &&
134 - // Set.prototype.keys
135 - Set.prototype != null &&
136 - typeof Set.prototype.keys === 'function' &&
137 - isNative(Set.prototype.keys)
138 - );
139 -
140 - var setItem;
141 - var getItem;
142 - var removeItem;
143 - var getItemIDs;
144 - var addRoot;
145 - var removeRoot;
146 - var getRootIDs;
118 + setItem = function(id, item) {
119 + var key = getKeyFromID(id);
120 + itemByKey[key] = item;
121 + };
122 + getItem = function(id) {
123 + var key = getKeyFromID(id);
124 + return itemByKey[key];
125 + };
126 + removeItem = function(id) {
127 + var key = getKeyFromID(id);
128 + delete itemByKey[key];
129 + };
130 + getItemIDs = function() {
131 + return Object.keys(itemByKey).map(getIDFromKey);
132 + };
133
148 - if (canUseCollections) {
149 - var itemMap = new Map();
150 - var rootIDSet = new Set();
134 + addRoot = function(id) {
135 + var key = getKeyFromID(id);
136 + rootByKey[key] = true;
137 + };
138 + removeRoot = function(id) {
139 + var key = getKeyFromID(id);
140 + delete rootByKey[key];
141 + };
142 + getRootIDs = function() {
143 + return Object.keys(rootByKey).map(getIDFromKey);
144 + };
145 +}
146
152 - setItem = function(id, item) {
153 - itemMap.set(id, item);
154 - };
155 - getItem = function(id) {
156 - return itemMap.get(id);
157 - };
158 - removeItem = function(id) {
159 - itemMap.delete(id);
160 - };
161 - getItemIDs = function() {
162 - return Array.from(itemMap.keys());
163 - };
147 +var unmountedIDs: Array<DebugID> = [];
148
165 - addRoot = function(id) {
166 - rootIDSet.add(id);
167 - };
168 - removeRoot = function(id) {
169 - rootIDSet.delete(id);
170 - };
171 - getRootIDs = function() {
172 - return Array.from(rootIDSet.keys());
173 - };
149 +function purgeDeep(id) {
150 + var item = getItem(id);
151 + if (item) {
152 + var {childIDs} = item;
153 + removeItem(id);
154 + childIDs.forEach(purgeDeep);
155 + }
156 +}
157
158 +function getDisplayName(element: ?ReactElement): string {
159 + if (element == null) {
160 + return '#empty';
161 + } else if (typeof element === 'string' || typeof element === 'number') {
162 + return '#text';
163 + } else if (typeof element.type === 'string') {
164 + return element.type;
165 } else {
176 - var itemByKey = {};
177 - var rootByKey = {};
166 + return element.type.displayName || element.type.name || 'Unknown';
167 + }
168 +}
169
179 - // Use non-numeric keys to prevent V8 performance issues:
180 - // https://github.com/facebook/react/pull/7232
181 - var getKeyFromID = function(id: DebugID): string {
182 - return '.' + id;
183 - };
184 - var getIDFromKey = function(key: string): DebugID {
185 - return parseInt(key.substr(1), 10);
186 - };
170 +function describeID(id: DebugID): string {
171 + const name = ReactComponentTreeHook.getDisplayName(id);
172 + const element = ReactComponentTreeHook.getElement(id);
173 + const ownerID = ReactComponentTreeHook.getOwnerID(id);
174 + let ownerName;
175
188 - setItem = function(id, item) {
189 - var key = getKeyFromID(id);
190 - itemByKey[key] = item;
191 - };
192 - getItem = function(id) {
193 - var key = getKeyFromID(id);
194 - return itemByKey[key];
195 - };
196 - removeItem = function(id) {
197 - var key = getKeyFromID(id);
198 - delete itemByKey[key];
199 - };
200 - getItemIDs = function() {
201 - return Object.keys(itemByKey).map(getIDFromKey);
202 - };
176 + if (ownerID) {
177 + ownerName = ReactComponentTreeHook.getDisplayName(ownerID);
178 + }
179 + warning(
180 + element,
181 + 'ReactComponentTreeHook: Missing React element for debugID %s when ' +
182 + 'building stack',
183 + id
184 + );
185 + if (element && name) {
186 + return describeComponentFrame(name || '', element._source, ownerName || '');
187 + }
188 + return '';
189 +}
190
204 - addRoot = function(id) {
205 - var key = getKeyFromID(id);
206 - rootByKey[key] = true;
207 - };
208 - removeRoot = function(id) {
209 - var key = getKeyFromID(id);
210 - delete rootByKey[key];
211 - };
212 - getRootIDs = function() {
213 - return Object.keys(rootByKey).map(getIDFromKey);
191 +var ReactComponentTreeHook = {
192 + onSetChildren(id: DebugID, nextChildIDs: Array<DebugID>): void {
193 + var item = getItem(id);
194 + invariant(item, 'Item must have been set');
195 + item.childIDs = nextChildIDs;
196 +
197 + for (var i = 0; i < nextChildIDs.length; i++) {
198 + var nextChildID = nextChildIDs[i];
199 + var nextChild = getItem(nextChildID);
200 + invariant(
201 + nextChild,
202 + 'Expected hook events to fire for the child ' +
203 + 'before its parent includes it in onSetChildren().'
204 + );
205 + invariant(
206 + nextChild.childIDs != null ||
207 + typeof nextChild.element !== 'object' ||
208 + nextChild.element == null,
209 + 'Expected onSetChildren() to fire for a container child ' +
210 + 'before its parent includes it in onSetChildren().'
211 + );
212 + invariant(
213 + nextChild.isMounted,
214 + 'Expected onMountComponent() to fire for the child ' +
215 + 'before its parent includes it in onSetChildren().'
216 + );
217 + if (nextChild.parentID == null) {
218 + nextChild.parentID = id;
219 + // TODO: This shouldn't be necessary but mounting a new root during in
220 + // componentWillMount currently causes not-yet-mounted components to
221 + // be purged from our tree data so their parent id is missing.
222 + }
223 + invariant(
224 + nextChild.parentID === id,
225 + 'Expected onBeforeMountComponent() parent and onSetChildren() to ' +
226 + 'be consistent (%s has parents %s and %s).',
227 + nextChildID,
228 + nextChild.parentID,
229 + id
230 + );
231 + }
232 + },
233 +
234 + onBeforeMountComponent(id: DebugID, element: ReactElement, parentID: DebugID): void {
235 + var item = {
236 + element,
237 + parentID,
238 + text: null,
239 + childIDs: [],
240 + isMounted: false,
241 + updateCount: 0,
242 };
215 - }
243 + setItem(id, item);
244 + },
245
217 - const unmountedIDs: Array<DebugID> = [];
246 + onBeforeUpdateComponent(id: DebugID, element: ReactElement): void {
247 + var item = getItem(id);
248 + if (!item || !item.isMounted) {
249 + // We may end up here as a result of setState() in componentWillUnmount().
250 + // In this case, ignore the element.
251 + return;
252 + }
253 + item.element = element;
254 + },
255
219 - const purgeDeep = function(id) {
256 + onMountComponent(id: DebugID): void {
257 var item = getItem(id);
221 - if (item) {
222 - var {childIDs} = item;
223 - removeItem(id);
224 - childIDs.forEach(purgeDeep);
258 + invariant(item, 'Item must have been set');
259 + item.isMounted = true;
260 + var isRoot = item.parentID === 0;
261 + if (isRoot) {
262 + addRoot(id);
263 }
226 - };
264 + },
265
228 - const getDisplayNameFromElement = function(element: ?ReactElement): string {
229 - if (element == null) {
230 - return '#empty';
231 - } else if (typeof element === 'string' || typeof element === 'number') {
232 - return '#text';
233 - } else if (typeof element.type === 'string') {
234 - return element.type;
235 - } else {
236 - return element.type.displayName || element.type.name || 'Unknown';
266 + onUpdateComponent(id: DebugID): void {
267 + var item = getItem(id);
268 + if (!item || !item.isMounted) {
269 + // We may end up here as a result of setState() in componentWillUnmount().
270 + // In this case, ignore the element.
271 + return;
272 }
238 - };
273 + item.updateCount++;
274 + },
275
240 - const getDisplayName = function(id: DebugID): ?string {
241 - var element = getElement(id);
242 - if (!element) {
243 - return null;
276 + onUnmountComponent(id: DebugID): void {
277 + var item = getItem(id);
278 + if (item) {
279 + // We need to check if it exists.
280 + // `item` might not exist if it is inside an error boundary, and a sibling
281 + // error boundary child threw while mounting. Then this instance never
282 + // got a chance to mount, but it still gets an unmounting event during
283 + // the error boundary cleanup.
284 + item.isMounted = false;
285 + var isRoot = item.parentID === 0;
286 + if (isRoot) {
287 + removeRoot(id);
288 + }
289 }
245 - return getDisplayNameFromElement(element);
246 - };
290 + unmountedIDs.push(id);
291 + },
292
248 - const getOwnerID = function(id: DebugID): ?DebugID {
249 - var element = getElement(id);
250 - if (!element || !element._owner) {
251 - return null;
293 + purgeUnmountedComponents(): void {
294 + if (ReactComponentTreeHook._preventPurging) {
295 + // Should only be used for testing.
296 + return;
297 }
253 - return element._owner._debugID;
254 - };
255 -
256 - const describeID = function(id: DebugID): string {
257 - var name = getDisplayName(id);
258 - var element = getElement(id);
259 - var ownerID = getOwnerID(id);
260 - var ownerName;
261 - if (ownerID) {
262 - ownerName = getDisplayName(ownerID);
298 +
299 + for (var i = 0; i < unmountedIDs.length; i++) {
300 + var id = unmountedIDs[i];
301 + purgeDeep(id);
302 }
264 - warning(
265 - element,
266 - 'ReactComponentTreeHook: Missing React element for debugID %s when ' +
267 - 'building stack',
268 - id
269 - );
270 - return describeComponentFrame(name, element && element._source, ownerName);
271 - };
303 + unmountedIDs.length = 0;
304 + },
305
273 - const getElement = function(id: DebugID): ?ReactElement {
306 + isMounted(id: DebugID): boolean {
307 var item = getItem(id);
275 - return item ? item.element : null;
276 - };
308 + return item ? item.isMounted : false;
309 + },
310
278 - const getParentID = function(id: DebugID): ?DebugID {
279 - var item = getItem(id);
280 - return item ? item.parentID : null;
281 - };
311 + getCurrentStackAddendum(topElement: ?ReactElement): string {
312 + var info = '';
313 + if (topElement) {
314 + var name = getDisplayName(topElement);
315 + var owner = topElement._owner;
316 + info += describeComponentFrame(
317 + name,
318 + topElement._source,
319 + owner && getComponentName(owner)
320 + );
321 + }
322 +
323 + var currentOwner = ReactCurrentOwner.current;
324 + if (currentOwner) {
325 + if (typeof currentOwner.tag === 'number') {
326 + const workInProgress = ((currentOwner : any) : Fiber);
327 + // Safe because if current owner exists, we are reconciling,
328 + // and it is guaranteed to be the work-in-progress version.
329 + info += getStackAddendumByWorkInProgressFiber(workInProgress);
330 + } else if (typeof currentOwner._debugID === 'number') {
331 + info += ReactComponentTreeHook.getStackAddendumByID(currentOwner._debugID);
332 + }
333 + }
334 + return info;
335 + },
336
283 - const getStackAddendumByID = function(id: ?DebugID): string {
337 + getStackAddendumByID(id: ?DebugID): string {
338 var info = '';
339 while (id) {
340 info += describeID(id);
287 - id = getParentID(id);
341 + id = ReactComponentTreeHook.getParentID(id);
342 }
343 return info;
290 - };
291 -
292 - ReactComponentTreeHook = Object.assign({}, ReactComponentTreeHook, {
293 - onSetChildren(id: DebugID, nextChildIDs: Array<DebugID>): void {
294 - var item = getItem(id);
295 - invariant(item, 'Item must have been set');
296 - item.childIDs = nextChildIDs;
297 -
298 - for (var i = 0; i < nextChildIDs.length; i++) {
299 - var nextChildID = nextChildIDs[i];
300 - var nextChild = getItem(nextChildID);
301 - invariant(
302 - nextChild,
303 - 'Expected hook events to fire for the child ' +
304 - 'before its parent includes it in onSetChildren().'
305 - );
306 - invariant(
307 - nextChild.childIDs != null ||
308 - typeof nextChild.element !== 'object' ||
309 - nextChild.element == null,
310 - 'Expected onSetChildren() to fire for a container child ' +
311 - 'before its parent includes it in onSetChildren().'
312 - );
313 - invariant(
314 - nextChild.isMounted,
315 - 'Expected onMountComponent() to fire for the child ' +
316 - 'before its parent includes it in onSetChildren().'
317 - );
318 - if (nextChild.parentID == null) {
319 - nextChild.parentID = id;
320 - // TODO: This shouldn't be necessary but mounting a new root during in
321 - // componentWillMount currently causes not-yet-mounted components to
322 - // be purged from our tree data so their parent id is missing.
323 - }
324 - invariant(
325 - nextChild.parentID === id,
326 - 'Expected onBeforeMountComponent() parent and onSetChildren() to ' +
327 - 'be consistent (%s has parents %s and %s).',
328 - nextChildID,
329 - nextChild.parentID,
330 - id
331 - );
332 - }
333 - },
334 -
335 - onBeforeMountComponent(id: DebugID, element: ReactElement, parentID: DebugID): void {
336 - var item = {
337 - element,
338 - parentID,
339 - text: null,
340 - childIDs: [],
341 - isMounted: false,
342 - updateCount: 0,
343 - };
344 - setItem(id, item);
345 - },
346 -
347 - onBeforeUpdateComponent(id: DebugID, element: ReactElement): void {
348 - var item = getItem(id);
349 - if (!item || !item.isMounted) {
350 - // We may end up here as a result of setState() in componentWillUnmount().
351 - // In this case, ignore the element.
352 - return;
353 - }
354 - item.element = element;
355 - },
344 + },
345
357 - onMountComponent(id: DebugID): void {
358 - var item = getItem(id);
359 - invariant(item, 'Item must have been set');
360 - item.isMounted = true;
361 - var isRoot = item.parentID === 0;
362 - if (isRoot) {
363 - addRoot(id);
364 - }
365 - },
366 -
367 - onUpdateComponent(id: DebugID): void {
368 - var item = getItem(id);
369 - if (!item || !item.isMounted) {
370 - // We may end up here as a result of setState() in componentWillUnmount().
371 - // In this case, ignore the element.
372 - return;
373 - }
374 - item.updateCount++;
375 - },
376 -
377 - onUnmountComponent(id: DebugID): void {
378 - var item = getItem(id);
379 - if (item) {
380 - // We need to check if it exists.
381 - // `item` might not exist if it is inside an error boundary, and a sibling
382 - // error boundary child threw while mounting. Then this instance never
383 - // got a chance to mount, but it still gets an unmounting event during
384 - // the error boundary cleanup.
385 - item.isMounted = false;
386 - var isRoot = item.parentID === 0;
387 - if (isRoot) {
388 - removeRoot(id);
389 - }
390 - }
391 - unmountedIDs.push(id);
392 - },
346 + getChildIDs(id: DebugID): Array<DebugID> {
347 + var item = getItem(id);
348 + return item ? item.childIDs : [];
349 + },
350
394 - purgeUnmountedComponents(): void {
395 - if (ReactComponentTreeHook._preventPurging) {
396 - // Should only be used for testing.
397 - return;
398 - }
351 + getDisplayName(id: DebugID): ?string {
352 + var element = ReactComponentTreeHook.getElement(id);
353 + if (!element) {
354 + return null;
355 + }
356 + return getDisplayName(element);
357 + },
358
400 - for (var i = 0; i < unmountedIDs.length; i++) {
401 - var id = unmountedIDs[i];
402 - purgeDeep(id);
403 - }
404 - unmountedIDs.length = 0;
405 - },
406 -
407 - isMounted(id: DebugID): boolean {
408 - var item = getItem(id);
409 - return item ? item.isMounted : false;
410 - },
411 -
412 - getCurrentStackAddendum(topElement: ?ReactElement): string {
413 - var info = '';
414 - if (topElement) {
415 - var name = getDisplayNameFromElement(topElement);
416 - var owner = topElement._owner;
417 - info += describeComponentFrame(
418 - name,
419 - topElement._source,
420 - owner && getComponentName(owner)
421 - );
422 - }
359 + getElement(id: DebugID): ?ReactElement {
360 + var item = getItem(id);
361 + return item ? item.element : null;
362 + },
363
424 - var currentOwner = ReactCurrentOwner.current;
425 - if (currentOwner) {
426 - if (typeof currentOwner.tag === 'number') {
427 - const workInProgress = ((currentOwner : any) : Fiber);
428 - // Safe because if current owner exists, we are reconciling,
429 - // and it is guaranteed to be the work-in-progress version.
430 - info += ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress);
431 - } else if (typeof currentOwner._debugID === 'number') {
432 - info += getStackAddendumByID(currentOwner._debugID);
433 - }
434 - }
435 - return info;
436 - },
364 + getOwnerID(id: DebugID): ?DebugID {
365 + var element = ReactComponentTreeHook.getElement(id);
366 + if (!element || !element._owner) {
367 + return null;
368 + }
369 + return element._owner._debugID;
370 + },
371
438 - getStackAddendumByID,
372 + getParentID(id: DebugID): ?DebugID {
373 + var item = getItem(id);
374 + return item ? item.parentID : null;
375 + },
376
440 - getChildIDs(id: DebugID): Array<DebugID> {
441 - var item = getItem(id);
442 - return item ? item.childIDs : [];
443 - },
377 + getSource(id: DebugID): ?Source {
378 + var item = getItem(id);
379 + var element = item ? item.element : null;
380 + var source = element != null ? element._source : null;
381 + return source;
382 + },
383
445 - getDisplayName(id: DebugID): ?string {
446 - var element = getElement(id);
447 - if (!element) {
448 - return null;
449 - }
450 - return getDisplayNameFromElement(element);
451 - },
452 -
453 - getElement,
454 -
455 - getOwnerID,
456 -
457 - getParentID,
458 -
459 - getSource(id: DebugID): ?Source {
460 - var item = getItem(id);
461 - var element = item ? item.element : null;
462 - var source = element != null ? element._source : null;
463 - return source;
464 - },
465 -
466 - getText(id: DebugID): ?string {
467 - var element = getElement(id);
468 - if (typeof element === 'string') {
469 - return element;
470 - } else if (typeof element === 'number') {
471 - return '' + element;
472 - } else {
473 - return null;
474 - }
475 - },
384 + getText(id: DebugID): ?string {
385 + var element = ReactComponentTreeHook.getElement(id);
386 + if (typeof element === 'string') {
387 + return element;
388 + } else if (typeof element === 'number') {
389 + return '' + element;
390 + } else {
391 + return null;
392 + }
393 + },
394
477 - getUpdateCount(id: DebugID): number {
478 - var item = getItem(id);
479 - return item ? item.updateCount : 0;
480 - },
395 + getUpdateCount(id: DebugID): number {
396 + var item = getItem(id);
397 + return item ? item.updateCount : 0;
398 + },
399
482 - getRootIDs,
483 - getRegisteredIDs: getItemIDs,
484 - });
485 -}
400 + getRootIDs,
401 + getRegisteredIDs: getItemIDs,
402 +};
403
404 module.exports = ReactComponentTreeHook;
src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js
+7 -2
@@ -12,7 +12,6 @@
12 'use strict';
13
14 var DOMProperty = require('DOMProperty');
15 -var ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
15 var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
16
17 var warning = require('fbjs/lib/warning');
@@ -20,10 +19,16 @@ var warning = require('fbjs/lib/warning');
19 var warnedProperties = {};
20 var rARIA = new RegExp('^(aria)-[' + DOMProperty.ATTRIBUTE_NAME_CHAR + ']*$');
21
22 +if (__DEV__) {
23 + var {
24 + getStackAddendumByID,
25 + } = require('react/lib/ReactComponentTreeHook');
26 +}
27 +
28 function getStackAddendum(debugID) {
29 if (debugID != null) {
30 // This can only happen on Stack
26 - return ReactComponentTreeHook.getStackAddendumByID(debugID);
31 + return getStackAddendumByID && getStackAddendumByID(debugID);
32 } else {
33 // This can only happen on Fiber
34 return ReactDebugCurrentFiber.getCurrentFiberStackAddendum();
src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js
+7 -3
@@ -11,17 +11,21 @@
11
12 'use strict';
13
14 -var ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
14 var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
16 -
15 var warning = require('fbjs/lib/warning');
16
17 +if (__DEV__) {
18 + var {
19 + getStackAddendumByID,
20 + } = require('react/lib/ReactComponentTreeHook');
21 +}
22 +
23 var didWarnValueNull = false;
24
25 function getStackAddendum(debugID) {
26 if (debugID != null) {
27 // This can only happen on Stack
24 - return ReactComponentTreeHook.getStackAddendumByID(debugID);
28 + return getStackAddendumByID && getStackAddendumByID(debugID);
29 } else {
30 // This can only happen on Fiber
31 return ReactDebugCurrentFiber.getCurrentFiberStackAddendum();
src/renderers/shared/ReactDebugTool.js
+20 -37
@@ -14,6 +14,7 @@
14
15 var ReactInvalidSetStateWarningHook = require('ReactInvalidSetStateWarningHook');
16 var ReactHostOperationHistoryHook = require('ReactHostOperationHistoryHook');
17 +var ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
18 var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
19
20 var performanceNow = require('fbjs/lib/performanceNow');
@@ -22,7 +23,6 @@ var warning = require('fbjs/lib/warning');
23 import type { ReactElement } from 'ReactElementType';
24 import type { DebugID } from 'ReactInstanceType';
25 import type { Operation } from 'ReactHostOperationHistoryHook';
25 -import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook';
26
27 type Hook = any;
28
@@ -67,20 +67,8 @@ export type FlushHistory = Array<HistoryItem>;
67 var ReactDebugTool = ((null: any): typeof ReactDebugTool);
68
69 if (__DEV__) {
70 - const hooks = [];
71 - const didHookThrowForEvent = {};
72 - const ReactComponentTreeHook: ComponentTreeHookDevType = (require('react/lib/ReactComponentTreeHook'): any);
73 - const {
74 - purgeUnmountedComponents,
75 - getOwnerID,
76 - getParentID,
77 - getDisplayName,
78 - getText,
79 - getUpdateCount,
80 - getChildIDs,
81 - getRegisteredIDs,
82 - getElement,
83 - } = ReactComponentTreeHook;
70 + var hooks = [];
71 + var didHookThrowForEvent = {};
72
73 const callHook = function(event, fn, context, arg1, arg2, arg3, arg4, arg5) {
74 try {
@@ -120,24 +108,22 @@ if (__DEV__) {
108 var lifeCycleTimerHasWarned = false;
109
110 const clearHistory = function() {
123 - if (purgeUnmountedComponents) {
124 - purgeUnmountedComponents();
125 - }
111 + ReactComponentTreeHook.purgeUnmountedComponents();
112 ReactHostOperationHistoryHook.clearHistory();
113 };
114
115 const getTreeSnapshot = function(registeredIDs) {
116 return registeredIDs.reduce((tree, id) => {
131 - var ownerID = getOwnerID(id);
132 - var parentID = getParentID(id);
117 + var ownerID = ReactComponentTreeHook.getOwnerID(id);
118 + var parentID = ReactComponentTreeHook.getParentID(id);
119 tree[id] = {
134 - displayName: getDisplayName(id),
135 - text: getText(id),
136 - updateCount: getUpdateCount(id),
137 - childIDs: getChildIDs(id),
120 + displayName: ReactComponentTreeHook.getDisplayName(id),
121 + text: ReactComponentTreeHook.getText(id),
122 + updateCount: ReactComponentTreeHook.getUpdateCount(id),
123 + childIDs: ReactComponentTreeHook.getChildIDs(id),
124 // Text nodes don't have owners but this is close enough.
125 ownerID: ownerID ||
140 - parentID && getOwnerID(parentID) ||
126 + parentID && ReactComponentTreeHook.getOwnerID(parentID) ||
127 0,
128 parentID,
129 };
@@ -158,16 +144,13 @@ if (__DEV__) {
144 }
145
146 if (previousMeasurements.length || previousOperations.length) {
161 - if (getRegisteredIDs) {
162 - var registeredIDs = getRegisteredIDs();
163 -
164 - flushHistory.push({
165 - duration: performanceNow() - previousStartTime,
166 - measurements: previousMeasurements || [],
167 - operations: previousOperations || [],
168 - treeSnapshot: getTreeSnapshot(registeredIDs),
169 - });
170 - }
147 + var registeredIDs = ReactComponentTreeHook.getRegisteredIDs();
148 + flushHistory.push({
149 + duration: performanceNow() - previousStartTime,
150 + measurements: previousMeasurements || [],
151 + operations: previousOperations || [],
152 + treeSnapshot: getTreeSnapshot(registeredIDs),
153 + });
154 }
155
156 clearHistory();
@@ -270,7 +253,7 @@ if (__DEV__) {
253 if (!isProfiling || !canUsePerformanceMeasure) {
254 return false;
255 }
273 - var element = getElement && getElement(debugID);
256 + var element = ReactComponentTreeHook.getElement(debugID);
257 if (element == null || typeof element !== 'object') {
258 return false;
259 }
@@ -297,7 +280,7 @@ if (__DEV__) {
280 }
281
282 var markName = `${debugID}::${markType}`;
300 - var displayName = getDisplayName && getDisplayName(debugID) || 'Unknown';
283 + var displayName = ReactComponentTreeHook.getDisplayName(debugID) || 'Unknown';
284
285 // Chrome has an issue of dropping markers recorded too fast:
286 // https://bugs.chromium.org/p/chromium/issues/detail?id=640652
src/renderers/shared/fiber/ReactDebugCurrentFiber.js
+1 -1
@@ -18,7 +18,7 @@ type LifeCyclePhase = 'render' | 'getChildContext';
18
19 if (__DEV__) {
20 var getComponentName = require('getComponentName');
21 - var { getStackAddendumByWorkInProgressFiber } = require('react/lib/ReactComponentTreeHook');
21 + var { getStackAddendumByWorkInProgressFiber } = require('ReactFiberComponentTreeHook');
22 }
23
24 function getCurrentFiberOwnerName() : string | null {
src/renderers/shared/fiber/ReactFiberScheduler.js
+1 -1
@@ -37,7 +37,7 @@ var {
37 const { reset } = require('ReactFiberStack');
38 var {
39 getStackAddendumByWorkInProgressFiber,
40 -} = require('react/lib/ReactComponentTreeHook');
40 +} = require('ReactFiberComponentTreeHook');
41 var { logCapturedError } = require('ReactFiberErrorLogger');
42 var { invokeGuardedCallback } = require('ReactErrorUtils');
43
src/renderers/shared/stack/reconciler/ReactRef.js
+3 -6
@@ -16,15 +16,12 @@ var ReactOwner = require('ReactOwner');
16
17 import type { ReactInstance } from 'ReactInstanceType';
18 import type { ReactElement } from 'ReactElementType';
19 -import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook';
19
20 var ReactRef = {};
21
22 if (__DEV__) {
23 var ReactCompositeComponentTypes = require('ReactCompositeComponentTypes');
25 - var {
26 - getStackAddendumByID,
27 - }: ComponentTreeHookDevType = (require('react/lib/ReactComponentTreeHook'): any);
24 + var ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
25 var warning = require('fbjs/lib/warning');
26
27 var warnedAboutStatelessRefs = {};
@@ -49,14 +46,14 @@ function attachRef(ref, component, owner) {
46 if (element && element._source) {
47 warningKey = element._source.fileName + ':' + element._source.lineNumber;
48 }
52 - if (!warnedAboutStatelessRefs[warningKey] && getStackAddendumByID) {
49 + if (!warnedAboutStatelessRefs[warningKey]) {
50 warnedAboutStatelessRefs[warningKey] = true;
51 warning(
52 false,
53 'Stateless function components cannot be given refs. ' +
54 'Attempts to access this ref will fail.%s%s',
55 info,
59 - getStackAddendumByID(component._debugID)
56 + ReactComponentTreeHook.getStackAddendumByID(component._debugID)
57 );
58 }
59 }
src/shared/ReactFiberComponentTreeHook.js new
+77
@@ -0,0 +1,77 @@
1 +/**
2 + * Copyright 2016-present, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @flow
10 + * @providesModule ReactFiberComponentTreeHook
11 + */
12 +
13 +'use strict';
14 +
15 +var ReactTypeOfWork = require('ReactTypeOfWork');
16 +var {
17 + IndeterminateComponent,
18 + FunctionalComponent,
19 + ClassComponent,
20 + HostComponent,
21 +} = ReactTypeOfWork;
22 +var getComponentName = require('getComponentName');
23 +
24 +import type { Fiber } from 'ReactFiber';
25 +import type { Source } from 'ReactElementType';
26 +
27 +function describeComponentFrame(name, source: Source, ownerName) {
28 + return '\n in ' + (name || 'Unknown') + (
29 + source ?
30 + ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' +
31 + source.lineNumber + ')' :
32 + ownerName ?
33 + ' (created by ' + ownerName + ')' :
34 + ''
35 + );
36 +}
37 +
38 +function describeFiber(fiber : Fiber) : string {
39 + switch (fiber.tag) {
40 + case IndeterminateComponent:
41 + case FunctionalComponent:
42 + case ClassComponent:
43 + case HostComponent:
44 + var owner = fiber._debugOwner;
45 + var source = fiber._debugSource;
46 + var name = getComponentName(fiber);
47 + var ownerName = null;
48 + if (owner) {
49 + ownerName = getComponentName(owner);
50 + }
51 + if (source) {
52 + return describeComponentFrame(name, source, ownerName);
53 + }
54 + return '';
55 + default:
56 + return '';
57 + }
58 +}
59 +
60 +// This function can only be called with a work-in-progress fiber and
61 +// only during begin or complete phase. Do not call it under any other
62 +// circumstances.
63 +function getStackAddendumByWorkInProgressFiber(workInProgress : Fiber) : string {
64 + var info = '';
65 + var node = workInProgress;
66 + do {
67 + info += describeFiber(node);
68 + // Otherwise this return pointer might point to the wrong tree:
69 + node = node.return;
70 + } while (node);
71 + return info;
72 +}
73 +
74 +module.exports = {
75 + getStackAddendumByWorkInProgressFiber,
76 + describeComponentFrame,
77 +};
src/shared/utils/flattenChildren.js
+2 -6
@@ -12,8 +12,6 @@
12
13 'use strict';
14
15 -import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook';
16 -
15 var KeyEscapeUtils = require('KeyEscapeUtils');
16 var traverseAllChildren = require('traverseAllChildren');
17 var warning = require('fbjs/lib/warning');
@@ -53,16 +51,14 @@ function flattenSingleChildIntoContext(
51 if (!ReactComponentTreeHook) {
52 ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
53 }
56 - const { getStackAddendumByID }: ComponentTreeHookDevType = (ReactComponentTreeHook: any);
57 -
58 - if (!keyUnique && getStackAddendumByID) {
54 + if (!keyUnique) {
55 warning(
56 false,
57 'flattenChildren(...): Encountered two children with the same key, ' +
58 '`%s`. Child keys must be unique; when two children share a key, only ' +
59 'the first child will be used.%s',
60 KeyEscapeUtils.unescape(name),
65 - getStackAddendumByID(selfDebugID)
61 + ReactComponentTreeHook.getStackAddendumByID(selfDebugID)
62 );
63 }
64 }
src/umd/ReactUMDEntry.js
+10 -1
@@ -17,8 +17,17 @@ var React = require('React');
17 var ReactUMDEntry = Object.assign({
18 __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
19 ReactCurrentOwner: require('react/lib/ReactCurrentOwner'),
20 - ReactComponentTreeHook: require('react/lib/ReactComponentTreeHook'),
20 },
21 }, React);
22
23 +if (__DEV__) {
24 + Object.assign(
25 + ReactUMDEntry.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
26 + {
27 + // ReactComponentTreeHook should not be included in production.
28 + ReactComponentTreeHook: require('react/lib/ReactComponentTreeHook'),
29 + }
30 + );
31 +}
32 +
33 module.exports = ReactUMDEntry;