From 0e23042e4bba436dceb8ec8ae42856429d2ea69e Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 2 Mar 2017 16:15:37 +0000 Subject: [PATCH 01/12] refactor of ReactComponentTreeHook to isolate dev methods --- .../classic/element/ReactDebugCurrentFrame.js | 13 +- .../hooks/ReactComponentTreeHook.js | 740 +++++++++--------- src/renderers/shared/ReactDebugTool.js | 22 +- .../shared/stack/reconciler/ReactRef.js | 2 +- src/shared/utils/flattenChildren.js | 2 +- 5 files changed, 393 insertions(+), 386 deletions(-) diff --git a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js index f4efdd672b..9cfbab11c4 100644 --- a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js +++ b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js @@ -18,12 +18,7 @@ import type { DebugID } from 'ReactInstanceType'; const ReactDebugCurrentFrame = {}; if (__DEV__) { - var { - getStackAddendumByID, - getStackAddendumByWorkInProgressFiber, - getCurrentStackAddendum, - } = require('ReactComponentTreeHook'); - + var ReactComponentTreeHook = require('ReactComponentTreeHook'); // Component that is being worked on ReactDebugCurrentFrame.current = (null : Fiber | DebugID | null); @@ -38,16 +33,16 @@ if (__DEV__) { if (typeof current === 'number') { // DebugID from Stack. const debugID = current; - stack = getStackAddendumByID(debugID); + stack = (ReactComponentTreeHook: any).getStackAddendumByID(debugID); } else if (typeof current.tag === 'number') { // This is a Fiber. // The stack will only be correct if this is a work in progress // version and we're calling it during reconciliation. const workInProgress = current; - stack = getStackAddendumByWorkInProgressFiber(workInProgress); + stack = ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress); } } else if (element !== null) { - stack = getCurrentStackAddendum(element); + stack = (ReactComponentTreeHook: any).getCurrentStackAddendum(element); } return stack; }; diff --git a/src/isomorphic/hooks/ReactComponentTreeHook.js b/src/isomorphic/hooks/ReactComponentTreeHook.js index 2359fd1c34..40c9a50279 100644 --- a/src/isomorphic/hooks/ReactComponentTreeHook.js +++ b/src/isomorphic/hooks/ReactComponentTreeHook.js @@ -12,7 +12,11 @@ 'use strict'; -var ReactCurrentOwner = require('ReactCurrentOwner'); +import type { ReactElement, Source } from 'ReactElementType'; +import type { DebugID } from 'ReactInstanceType'; +import type { Fiber } from 'ReactFiber'; + +var getComponentName = require('getComponentName'); var ReactTypeOfWork = require('ReactTypeOfWork'); var { IndeterminateComponent, @@ -21,144 +25,6 @@ var { HostComponent, } = ReactTypeOfWork; -var getComponentName = require('getComponentName'); -var invariant = require('fbjs/lib/invariant'); -var warning = require('fbjs/lib/warning'); - -import type { ReactElement, Source } from 'ReactElementType'; -import type { DebugID } from 'ReactInstanceType'; -import type { Fiber } from 'ReactFiber'; - -function isNative(fn) { - // Based on isNative() from Lodash - var funcToString = Function.prototype.toString; - var hasOwnProperty = Object.prototype.hasOwnProperty; - var reIsNative = RegExp('^' + funcToString - // Take an example native function source for comparison - .call(hasOwnProperty) - // Strip regex characters so we can use it for regex - .replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') - // Remove hasOwnProperty from the template to make it generic - .replace( - /hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, - '$1.*?' - ) + '$' - ); - try { - var source = funcToString.call(fn); - return reIsNative.test(source); - } catch (err) { - return false; - } -} - -var canUseCollections = ( - // Array.from - typeof Array.from === 'function' && - // Map - typeof Map === 'function' && - isNative(Map) && - // Map.prototype.keys - Map.prototype != null && - typeof Map.prototype.keys === 'function' && - isNative(Map.prototype.keys) && - // Set - typeof Set === 'function' && - isNative(Set) && - // Set.prototype.keys - Set.prototype != null && - typeof Set.prototype.keys === 'function' && - isNative(Set.prototype.keys) -); - -var setItem; -var getItem; -var removeItem; -var getItemIDs; -var addRoot; -var removeRoot; -var getRootIDs; - -if (canUseCollections) { - var itemMap = new Map(); - var rootIDSet = new Set(); - - setItem = function(id, item) { - itemMap.set(id, item); - }; - getItem = function(id) { - return itemMap.get(id); - }; - removeItem = function(id) { - itemMap.delete(id); - }; - getItemIDs = function() { - return Array.from(itemMap.keys()); - }; - - addRoot = function(id) { - rootIDSet.add(id); - }; - removeRoot = function(id) { - rootIDSet.delete(id); - }; - getRootIDs = function() { - return Array.from(rootIDSet.keys()); - }; - -} else { - var itemByKey = {}; - var rootByKey = {}; - - // Use non-numeric keys to prevent V8 performance issues: - // https://github.com/facebook/react/pull/7232 - var getKeyFromID = function(id: DebugID): string { - return '.' + id; - }; - var getIDFromKey = function(key: string): DebugID { - return parseInt(key.substr(1), 10); - }; - - setItem = function(id, item) { - var key = getKeyFromID(id); - itemByKey[key] = item; - }; - getItem = function(id) { - var key = getKeyFromID(id); - return itemByKey[key]; - }; - removeItem = function(id) { - var key = getKeyFromID(id); - delete itemByKey[key]; - }; - getItemIDs = function() { - return Object.keys(itemByKey).map(getIDFromKey); - }; - - addRoot = function(id) { - var key = getKeyFromID(id); - rootByKey[key] = true; - }; - removeRoot = function(id) { - var key = getKeyFromID(id); - delete rootByKey[key]; - }; - getRootIDs = function() { - return Object.keys(rootByKey).map(getIDFromKey); - }; -} - -var unmountedIDs: Array = []; - -function purgeDeep(id) { - var item = getItem(id); - if (item) { - var {childIDs} = item; - removeItem(id); - childIDs.forEach(purgeDeep); - } -} - function describeComponentFrame(name, source, ownerName) { return '\n in ' + (name || 'Unknown') + ( source ? @@ -170,35 +36,6 @@ function describeComponentFrame(name, source, ownerName) { ); } -function getDisplayName(element: ?ReactElement): string { - if (element == null) { - return '#empty'; - } else if (typeof element === 'string' || typeof element === 'number') { - return '#text'; - } else if (typeof element.type === 'string') { - return element.type; - } else { - return element.type.displayName || element.type.name || 'Unknown'; - } -} - -function describeID(id: DebugID): string { - var name = ReactComponentTreeHook.getDisplayName(id); - var element = ReactComponentTreeHook.getElement(id); - var ownerID = ReactComponentTreeHook.getOwnerID(id); - var ownerName; - if (ownerID) { - ownerName = ReactComponentTreeHook.getDisplayName(ownerID); - } - warning( - element, - 'ReactComponentTreeHook: Missing React element for debugID %s when ' + - 'building stack', - id - ); - return describeComponentFrame(name, element && element._source, ownerName); -} - function describeFiber(fiber : Fiber) : string { switch (fiber.tag) { case IndeterminateComponent: @@ -219,160 +56,6 @@ function describeFiber(fiber : Fiber) : string { } var ReactComponentTreeHook = { - onSetChildren(id: DebugID, nextChildIDs: Array): void { - var item = getItem(id); - invariant(item, 'Item must have been set'); - item.childIDs = nextChildIDs; - - for (var i = 0; i < nextChildIDs.length; i++) { - var nextChildID = nextChildIDs[i]; - var nextChild = getItem(nextChildID); - invariant( - nextChild, - 'Expected hook events to fire for the child ' + - 'before its parent includes it in onSetChildren().' - ); - invariant( - nextChild.childIDs != null || - typeof nextChild.element !== 'object' || - nextChild.element == null, - 'Expected onSetChildren() to fire for a container child ' + - 'before its parent includes it in onSetChildren().' - ); - invariant( - nextChild.isMounted, - 'Expected onMountComponent() to fire for the child ' + - 'before its parent includes it in onSetChildren().' - ); - if (nextChild.parentID == null) { - nextChild.parentID = id; - // TODO: This shouldn't be necessary but mounting a new root during in - // componentWillMount currently causes not-yet-mounted components to - // be purged from our tree data so their parent id is missing. - } - invariant( - nextChild.parentID === id, - 'Expected onBeforeMountComponent() parent and onSetChildren() to ' + - 'be consistent (%s has parents %s and %s).', - nextChildID, - nextChild.parentID, - id - ); - } - }, - - onBeforeMountComponent(id: DebugID, element: ReactElement, parentID: DebugID): void { - var item = { - element, - parentID, - text: null, - childIDs: [], - isMounted: false, - updateCount: 0, - }; - setItem(id, item); - }, - - onBeforeUpdateComponent(id: DebugID, element: ReactElement): void { - var item = getItem(id); - if (!item || !item.isMounted) { - // We may end up here as a result of setState() in componentWillUnmount(). - // In this case, ignore the element. - return; - } - item.element = element; - }, - - onMountComponent(id: DebugID): void { - var item = getItem(id); - invariant(item, 'Item must have been set'); - item.isMounted = true; - var isRoot = item.parentID === 0; - if (isRoot) { - addRoot(id); - } - }, - - onUpdateComponent(id: DebugID): void { - var item = getItem(id); - if (!item || !item.isMounted) { - // We may end up here as a result of setState() in componentWillUnmount(). - // In this case, ignore the element. - return; - } - item.updateCount++; - }, - - onUnmountComponent(id: DebugID): void { - var item = getItem(id); - if (item) { - // We need to check if it exists. - // `item` might not exist if it is inside an error boundary, and a sibling - // error boundary child threw while mounting. Then this instance never - // got a chance to mount, but it still gets an unmounting event during - // the error boundary cleanup. - item.isMounted = false; - var isRoot = item.parentID === 0; - if (isRoot) { - removeRoot(id); - } - } - unmountedIDs.push(id); - }, - - purgeUnmountedComponents(): void { - if (ReactComponentTreeHook._preventPurging) { - // Should only be used for testing. - return; - } - - for (var i = 0; i < unmountedIDs.length; i++) { - var id = unmountedIDs[i]; - purgeDeep(id); - } - unmountedIDs.length = 0; - }, - - isMounted(id: DebugID): boolean { - var item = getItem(id); - return item ? item.isMounted : false; - }, - - getCurrentStackAddendum(topElement: ?ReactElement): string { - var info = ''; - if (topElement) { - var name = getDisplayName(topElement); - var owner = topElement._owner; - info += describeComponentFrame( - name, - topElement._source, - owner && getComponentName(owner) - ); - } - - var currentOwner = ReactCurrentOwner.current; - if (currentOwner) { - if (typeof currentOwner.tag === 'number') { - const workInProgress = ((currentOwner : any) : Fiber); - // Safe because if current owner exists, we are reconciling, - // and it is guaranteed to be the work-in-progress version. - info += ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress); - } else if (typeof currentOwner._debugID === 'number') { - info += ReactComponentTreeHook.getStackAddendumByID(currentOwner._debugID); - } - } - return info; - }, - - getStackAddendumByID(id: ?DebugID): string { - var info = ''; - while (id) { - info += describeID(id); - id = ReactComponentTreeHook.getParentID(id); - } - return info; - }, - // This function can only be called with a work-in-progress fiber and // only during begin or complete phase. Do not call it under any other // circumstances. @@ -386,63 +69,392 @@ var ReactComponentTreeHook = { } while (node); return info; }, +}; - getChildIDs(id: DebugID): Array { - var item = getItem(id); - return item ? item.childIDs : []; - }, +if (__DEV__) { + var ReactCurrentOwner = require('ReactCurrentOwner'); + var invariant = require('fbjs/lib/invariant'); + var warning = require('fbjs/lib/warning'); - getDisplayName(id: DebugID): ?string { - var element = ReactComponentTreeHook.getElement(id); - if (!element) { - return null; + var isNative = function(fn) { + // Based on isNative() from Lodash + var funcToString = Function.prototype.toString; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var reIsNative = RegExp('^' + funcToString + // Take an example native function source for comparison + .call(hasOwnProperty) + // Strip regex characters so we can use it for regex + .replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + // Remove hasOwnProperty from the template to make it generic + .replace( + /hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, + '$1.*?' + ) + '$' + ); + try { + var source = funcToString.call(fn); + return reIsNative.test(source); + } catch (err) { + return false; } - return getDisplayName(element); - }, + }; - getElement(id: DebugID): ?ReactElement { + var canUseCollections = ( + // Array.from + typeof Array.from === 'function' && + // Map + typeof Map === 'function' && + isNative(Map) && + // Map.prototype.keys + Map.prototype != null && + typeof Map.prototype.keys === 'function' && + isNative(Map.prototype.keys) && + // Set + typeof Set === 'function' && + isNative(Set) && + // Set.prototype.keys + Set.prototype != null && + typeof Set.prototype.keys === 'function' && + isNative(Set.prototype.keys) + ); + + var setItem; + var getItem; + var removeItem; + var getItemIDs; + var addRoot; + var removeRoot; + var getRootIDs; + + if (canUseCollections) { + var itemMap = new Map(); + var rootIDSet = new Set(); + + setItem = function(id, item) { + itemMap.set(id, item); + }; + getItem = function(id) { + return itemMap.get(id); + }; + removeItem = function(id) { + itemMap.delete(id); + }; + getItemIDs = function() { + return Array.from(itemMap.keys()); + }; + + addRoot = function(id) { + rootIDSet.add(id); + }; + removeRoot = function(id) { + rootIDSet.delete(id); + }; + getRootIDs = function() { + return Array.from(rootIDSet.keys()); + }; + + } else { + var itemByKey = {}; + var rootByKey = {}; + + // Use non-numeric keys to prevent V8 performance issues: + // https://github.com/facebook/react/pull/7232 + var getKeyFromID = function(id: DebugID): string { + return '.' + id; + }; + var getIDFromKey = function(key: string): DebugID { + return parseInt(key.substr(1), 10); + }; + + setItem = function(id, item) { + var key = getKeyFromID(id); + itemByKey[key] = item; + }; + getItem = function(id) { + var key = getKeyFromID(id); + return itemByKey[key]; + }; + removeItem = function(id) { + var key = getKeyFromID(id); + delete itemByKey[key]; + }; + getItemIDs = function() { + return Object.keys(itemByKey).map(getIDFromKey); + }; + + addRoot = function(id) { + var key = getKeyFromID(id); + rootByKey[key] = true; + }; + removeRoot = function(id) { + var key = getKeyFromID(id); + delete rootByKey[key]; + }; + getRootIDs = function() { + return Object.keys(rootByKey).map(getIDFromKey); + }; + } + + var unmountedIDs: Array = []; + + var purgeDeep = function(id) { var item = getItem(id); - return item ? item.element : null; - }, + if (item) { + var {childIDs} = item; + removeItem(id); + childIDs.forEach(purgeDeep); + } + }; - getOwnerID(id: DebugID): ?DebugID { - var element = ReactComponentTreeHook.getElement(id); + var getDisplayName = function(element: ?ReactElement): string { + if (element == null) { + return '#empty'; + } else if (typeof element === 'string' || typeof element === 'number') { + return '#text'; + } else if (typeof element.type === 'string') { + return element.type; + } else { + return element.type.displayName || element.type.name || 'Unknown'; + } + }; + + var describeID = function(id: DebugID): string { + var name = getDisplayName((id: any)); + var element = getElement(id); + var ownerID: any = getOwnerID(id); + var ownerName; + if (ownerID) { + ownerName = getDisplayName(ownerID); + } + warning( + element, + 'ReactComponentTreeHook: Missing React element for debugID %s when ' + + 'building stack', + id + ); + return describeComponentFrame(name, element && element._source, ownerName); + }; + + var getOwnerID = function(id: DebugID): ?DebugID { + var element = getElement(id); if (!element || !element._owner) { return null; } return element._owner._debugID; - }, + }; - getParentID(id: DebugID): ?DebugID { + var getElement = function(id: DebugID): ?ReactElement { + var item = getItem(id); + return item ? item.element : null; + }; + + var getParentID = function(id: DebugID): ?DebugID { var item = getItem(id); return item ? item.parentID : null; - }, - - getSource(id: DebugID): ?Source { - var item = getItem(id); - var element = item ? item.element : null; - var source = element != null ? element._source : null; - return source; - }, - - getText(id: DebugID): ?string { - var element = ReactComponentTreeHook.getElement(id); - if (typeof element === 'string') { - return element; - } else if (typeof element === 'number') { - return '' + element; - } else { - return null; + }; + + var getStackAddendumByID = function(id: ?DebugID): string { + var info = ''; + while (id) { + info += describeID(id); + id = getParentID(id); } - }, + return info; + }; - getUpdateCount(id: DebugID): number { - var item = getItem(id); - return item ? item.updateCount : 0; - }, + ReactComponentTreeHook = Object.assign({}, ReactComponentTreeHook, { + onSetChildren(id: DebugID, nextChildIDs: Array): void { + var item = getItem(id); + invariant(item, 'Item must have been set'); + item.childIDs = nextChildIDs; - getRootIDs, - getRegisteredIDs: getItemIDs, -}; + for (var i = 0; i < nextChildIDs.length; i++) { + var nextChildID = nextChildIDs[i]; + var nextChild = getItem(nextChildID); + invariant( + nextChild, + 'Expected hook events to fire for the child ' + + 'before its parent includes it in onSetChildren().' + ); + invariant( + nextChild.childIDs != null || + typeof nextChild.element !== 'object' || + nextChild.element == null, + 'Expected onSetChildren() to fire for a container child ' + + 'before its parent includes it in onSetChildren().' + ); + invariant( + nextChild.isMounted, + 'Expected onMountComponent() to fire for the child ' + + 'before its parent includes it in onSetChildren().' + ); + if (nextChild.parentID == null) { + nextChild.parentID = id; + // TODO: This shouldn't be necessary but mounting a new root during in + // componentWillMount currently causes not-yet-mounted components to + // be purged from our tree data so their parent id is missing. + } + invariant( + nextChild.parentID === id, + 'Expected onBeforeMountComponent() parent and onSetChildren() to ' + + 'be consistent (%s has parents %s and %s).', + nextChildID, + nextChild.parentID, + id + ); + } + }, + + onBeforeMountComponent(id: DebugID, element: ReactElement, parentID: DebugID): void { + var item = { + element, + parentID, + text: null, + childIDs: [], + isMounted: false, + updateCount: 0, + }; + setItem(id, item); + }, + + onBeforeUpdateComponent(id: DebugID, element: ReactElement): void { + var item = getItem(id); + if (!item || !item.isMounted) { + // We may end up here as a result of setState() in componentWillUnmount(). + // In this case, ignore the element. + return; + } + item.element = element; + }, + + onMountComponent(id: DebugID): void { + var item = getItem(id); + invariant(item, 'Item must have been set'); + item.isMounted = true; + var isRoot = item.parentID === 0; + if (isRoot) { + addRoot(id); + } + }, + + onUpdateComponent(id: DebugID): void { + var item = getItem(id); + if (!item || !item.isMounted) { + // We may end up here as a result of setState() in componentWillUnmount(). + // In this case, ignore the element. + return; + } + item.updateCount++; + }, + + onUnmountComponent(id: DebugID): void { + var item = getItem(id); + if (item) { + // We need to check if it exists. + // `item` might not exist if it is inside an error boundary, and a sibling + // error boundary child threw while mounting. Then this instance never + // got a chance to mount, but it still gets an unmounting event during + // the error boundary cleanup. + item.isMounted = false; + var isRoot = item.parentID === 0; + if (isRoot) { + removeRoot(id); + } + } + unmountedIDs.push(id); + }, + + purgeUnmountedComponents(): void { + if (ReactComponentTreeHook._preventPurging) { + // Should only be used for testing. + return; + } + + for (var i = 0; i < unmountedIDs.length; i++) { + var id = unmountedIDs[i]; + purgeDeep(id); + } + unmountedIDs.length = 0; + }, + + isMounted(id: DebugID): boolean { + var item = getItem(id); + return item ? item.isMounted : false; + }, + + getCurrentStackAddendum(topElement: ?ReactElement): string { + var info = ''; + if (topElement) { + var name = getDisplayName(topElement); + var owner = topElement._owner; + info += describeComponentFrame( + name, + topElement._source, + owner && getComponentName(owner) + ); + } + + var currentOwner = ReactCurrentOwner.current; + if (currentOwner) { + if (typeof currentOwner.tag === 'number') { + const workInProgress = ((currentOwner : any) : Fiber); + // Safe because if current owner exists, we are reconciling, + // and it is guaranteed to be the work-in-progress version. + info += ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress); + } else if (typeof currentOwner._debugID === 'number') { + info += getStackAddendumByID(currentOwner._debugID); + } + } + return info; + }, + + getStackAddendumByID, + + getChildIDs(id: DebugID): Array { + var item = getItem(id); + return item ? item.childIDs : []; + }, + + getDisplayName(id: DebugID): ?string { + var element = getElement(id); + if (!element) { + return null; + } + return getDisplayName(element); + }, + + getElement, + + getOwnerID, + + getParentID, + + getSource(id: DebugID): ?Source { + var item = getItem(id); + var element = item ? item.element : null; + var source = element != null ? element._source : null; + return source; + }, + + getText(id: DebugID): ?string { + var element = getElement(id); + if (typeof element === 'string') { + return element; + } else if (typeof element === 'number') { + return '' + element; + } else { + return null; + } + }, + + getUpdateCount(id: DebugID): number { + var item = getItem(id); + return item ? item.updateCount : 0; + }, + + getRootIDs, + getRegisteredIDs: getItemIDs, + }); +} module.exports = ReactComponentTreeHook; diff --git a/src/renderers/shared/ReactDebugTool.js b/src/renderers/shared/ReactDebugTool.js index 6a3781f947..bae00db165 100644 --- a/src/renderers/shared/ReactDebugTool.js +++ b/src/renderers/shared/ReactDebugTool.js @@ -108,22 +108,22 @@ if (__DEV__) { var lifeCycleTimerHasWarned = false; const clearHistory = function() { - ReactComponentTreeHook.purgeUnmountedComponents(); + (ReactComponentTreeHook: any).purgeUnmountedComponents(); ReactHostOperationHistoryHook.clearHistory(); }; const getTreeSnapshot = function(registeredIDs) { return registeredIDs.reduce((tree, id) => { - var ownerID = ReactComponentTreeHook.getOwnerID(id); - var parentID = ReactComponentTreeHook.getParentID(id); + var ownerID = (ReactComponentTreeHook: any).getOwnerID(id); + var parentID = (ReactComponentTreeHook: any).getParentID(id); tree[id] = { - displayName: ReactComponentTreeHook.getDisplayName(id), - text: ReactComponentTreeHook.getText(id), - updateCount: ReactComponentTreeHook.getUpdateCount(id), - childIDs: ReactComponentTreeHook.getChildIDs(id), + displayName: (ReactComponentTreeHook: any).getDisplayName(id), + text: (ReactComponentTreeHook: any).getText(id), + updateCount: (ReactComponentTreeHook: any).getUpdateCount(id), + childIDs: (ReactComponentTreeHook: any).getChildIDs(id), // Text nodes don't have owners but this is close enough. ownerID: ownerID || - parentID && ReactComponentTreeHook.getOwnerID(parentID) || + parentID && (ReactComponentTreeHook: any).getOwnerID(parentID) || 0, parentID, }; @@ -144,7 +144,7 @@ if (__DEV__) { } if (previousMeasurements.length || previousOperations.length) { - var registeredIDs = ReactComponentTreeHook.getRegisteredIDs(); + var registeredIDs = (ReactComponentTreeHook: any).getRegisteredIDs(); flushHistory.push({ duration: performanceNow() - previousStartTime, measurements: previousMeasurements || [], @@ -253,7 +253,7 @@ if (__DEV__) { if (!isProfiling || !canUsePerformanceMeasure) { return false; } - var element = ReactComponentTreeHook.getElement(debugID); + var element = (ReactComponentTreeHook: any).getElement(debugID); if (element == null || typeof element !== 'object') { return false; } @@ -280,7 +280,7 @@ if (__DEV__) { } var markName = `${debugID}::${markType}`; - var displayName = ReactComponentTreeHook.getDisplayName(debugID) || 'Unknown'; + var displayName = (ReactComponentTreeHook: any).getDisplayName(debugID) || 'Unknown'; // Chrome has an issue of dropping markers recorded too fast: // https://bugs.chromium.org/p/chromium/issues/detail?id=640652 diff --git a/src/renderers/shared/stack/reconciler/ReactRef.js b/src/renderers/shared/stack/reconciler/ReactRef.js index 120c6dddbd..1a088e32da 100644 --- a/src/renderers/shared/stack/reconciler/ReactRef.js +++ b/src/renderers/shared/stack/reconciler/ReactRef.js @@ -53,7 +53,7 @@ function attachRef(ref, component, owner) { 'Stateless function components cannot be given refs. ' + 'Attempts to access this ref will fail.%s%s', info, - ReactComponentTreeHook.getStackAddendumByID(component._debugID) + (ReactComponentTreeHook: any).getStackAddendumByID(component._debugID) ); } } diff --git a/src/shared/utils/flattenChildren.js b/src/shared/utils/flattenChildren.js index d07630580f..7bc7d7c94a 100644 --- a/src/shared/utils/flattenChildren.js +++ b/src/shared/utils/flattenChildren.js @@ -58,7 +58,7 @@ function flattenSingleChildIntoContext( '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), - ReactComponentTreeHook.getStackAddendumByID(selfDebugID) + (ReactComponentTreeHook: any).getStackAddendumByID(selfDebugID) ); } } From e8f3f92a88ac90ba4c95c8fae1b563acf29d959e Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 2 Mar 2017 17:53:09 +0000 Subject: [PATCH 02/12] fixed flow issues and bug resulting in failing tests --- .../classic/element/ReactDebugCurrentFrame.js | 17 +++-- .../hooks/ReactComponentTreeHook.js | 64 +++++++++++++------ src/renderers/shared/ReactDebugTool.js | 55 ++++++++++------ .../shared/stack/reconciler/ReactRef.js | 6 +- src/shared/utils/flattenChildren.js | 4 +- 5 files changed, 98 insertions(+), 48 deletions(-) diff --git a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js index 9cfbab11c4..7a36e4f257 100644 --- a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js +++ b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js @@ -14,11 +14,16 @@ import type { Fiber } from 'ReactFiber'; import type { DebugID } from 'ReactInstanceType'; +import type { ComponentTreeHookType } from '../../hooks/ReactComponentTreeHook'; const ReactDebugCurrentFrame = {}; if (__DEV__) { - var ReactComponentTreeHook = require('ReactComponentTreeHook'); + const { + getStackAddendumByID, + getStackAddendumByWorkInProgressFiber, + getCurrentStackAddendum, + }: ComponentTreeHookType = require('ReactComponentTreeHook'); // Component that is being worked on ReactDebugCurrentFrame.current = (null : Fiber | DebugID | null); @@ -33,16 +38,20 @@ if (__DEV__) { if (typeof current === 'number') { // DebugID from Stack. const debugID = current; - stack = (ReactComponentTreeHook: any).getStackAddendumByID(debugID); + if (getStackAddendumByID) { + stack = getStackAddendumByID(debugID); + } } else if (typeof current.tag === 'number') { // This is a Fiber. // The stack will only be correct if this is a work in progress // version and we're calling it during reconciliation. const workInProgress = current; - stack = ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress); + stack = getStackAddendumByWorkInProgressFiber(workInProgress); } } else if (element !== null) { - stack = (ReactComponentTreeHook: any).getCurrentStackAddendum(element); + if (getCurrentStackAddendum) { + stack = getCurrentStackAddendum(element); + } } return stack; }; diff --git a/src/isomorphic/hooks/ReactComponentTreeHook.js b/src/isomorphic/hooks/ReactComponentTreeHook.js index 40c9a50279..a268651098 100644 --- a/src/isomorphic/hooks/ReactComponentTreeHook.js +++ b/src/isomorphic/hooks/ReactComponentTreeHook.js @@ -55,7 +55,21 @@ function describeFiber(fiber : Fiber) : string { } } -var ReactComponentTreeHook = { +export type ComponentTreeHookType = { + getStackAddendumByWorkInProgressFiber: (Fiber) => any, + getStackAddendumByID?: () => any, + getCurrentStackAddendum?: () => any, + purgeUnmountedComponents?: () => any, + getOwnerID?: (DebugID) => any, + getParentID?: (DebugID) => any, + getDisplayName?: (DebugID) => any, + getText?: (DebugID) => any, + getUpdateCount?: (DebugID) => any, + getChildIDs?: (DebugID) => any, + getRegisteredIDs?: () => any, +}; + +var ReactComponentTreeHook: ComponentTreeHookType = { // This function can only be called with a work-in-progress fiber and // only during begin or complete phase. Do not call it under any other // circumstances. @@ -195,9 +209,9 @@ if (__DEV__) { }; } - var unmountedIDs: Array = []; + const unmountedIDs: Array = []; - var purgeDeep = function(id) { + const purgeDeep = function(id) { var item = getItem(id); if (item) { var {childIDs} = item; @@ -206,7 +220,7 @@ if (__DEV__) { } }; - var getDisplayName = function(element: ?ReactElement): string { + const getDisplayNameFromElement = function(element: ?ReactElement): string { if (element == null) { return '#empty'; } else if (typeof element === 'string' || typeof element === 'number') { @@ -218,10 +232,26 @@ if (__DEV__) { } }; - var describeID = function(id: DebugID): string { - var name = getDisplayName((id: any)); + const getDisplayName = function(id: DebugID): ?string { var element = getElement(id); - var ownerID: any = getOwnerID(id); + if (!element) { + return null; + } + return getDisplayNameFromElement(element); + }; + + const getOwnerID = function(id: DebugID): ?DebugID { + var element = getElement(id); + if (!element || !element._owner) { + return null; + } + return element._owner._debugID; + }; + + const describeID = function(id: DebugID): string { + var name = getDisplayName(id); + var element = getElement(id); + var ownerID = getOwnerID(id); var ownerName; if (ownerID) { ownerName = getDisplayName(ownerID); @@ -235,25 +265,17 @@ if (__DEV__) { return describeComponentFrame(name, element && element._source, ownerName); }; - var getOwnerID = function(id: DebugID): ?DebugID { - var element = getElement(id); - if (!element || !element._owner) { - return null; - } - return element._owner._debugID; - }; - - var getElement = function(id: DebugID): ?ReactElement { + const getElement = function(id: DebugID): ?ReactElement { var item = getItem(id); return item ? item.element : null; }; - var getParentID = function(id: DebugID): ?DebugID { + const getParentID = function(id: DebugID): ?DebugID { var item = getItem(id); return item ? item.parentID : null; }; - - var getStackAddendumByID = function(id: ?DebugID): string { + + const getStackAddendumByID = function(id: ?DebugID): string { var info = ''; while (id) { info += describeID(id); @@ -385,7 +407,7 @@ if (__DEV__) { getCurrentStackAddendum(topElement: ?ReactElement): string { var info = ''; if (topElement) { - var name = getDisplayName(topElement); + var name = getDisplayNameFromElement(topElement); var owner = topElement._owner; info += describeComponentFrame( name, @@ -420,7 +442,7 @@ if (__DEV__) { if (!element) { return null; } - return getDisplayName(element); + return getDisplayNameFromElement(element); }, getElement, diff --git a/src/renderers/shared/ReactDebugTool.js b/src/renderers/shared/ReactDebugTool.js index bae00db165..1d07610ade 100644 --- a/src/renderers/shared/ReactDebugTool.js +++ b/src/renderers/shared/ReactDebugTool.js @@ -14,7 +14,6 @@ var ReactInvalidSetStateWarningHook = require('ReactInvalidSetStateWarningHook'); var ReactHostOperationHistoryHook = require('ReactHostOperationHistoryHook'); -var ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook'); var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment'); var performanceNow = require('fbjs/lib/performanceNow'); @@ -67,8 +66,19 @@ export type FlushHistory = Array; var ReactDebugTool = ((null: any): typeof ReactDebugTool); if (__DEV__) { - var hooks = []; - var didHookThrowForEvent = {}; + const hooks = []; + const didHookThrowForEvent = {}; + const ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook'); + const { + purgeUnmountedComponents, + getOwnerID, + getParentID, + getDisplayName, + getText, + getUpdateCount, + getChildIDs, + getRegisteredIDs, + } = ReactComponentTreeHook; const callHook = function(event, fn, context, arg1, arg2, arg3, arg4, arg5) { try { @@ -108,22 +118,24 @@ if (__DEV__) { var lifeCycleTimerHasWarned = false; const clearHistory = function() { - (ReactComponentTreeHook: any).purgeUnmountedComponents(); + if (purgeUnmountedComponents) { + purgeUnmountedComponents(); + } ReactHostOperationHistoryHook.clearHistory(); }; const getTreeSnapshot = function(registeredIDs) { - return registeredIDs.reduce((tree, id) => { - var ownerID = (ReactComponentTreeHook: any).getOwnerID(id); - var parentID = (ReactComponentTreeHook: any).getParentID(id); + return registeredIDs && registeredIDs.reduce((tree, id) => { + var ownerID = getOwnerID && getOwnerID(id); + var parentID = getParentID && getParentID(id); tree[id] = { - displayName: (ReactComponentTreeHook: any).getDisplayName(id), - text: (ReactComponentTreeHook: any).getText(id), - updateCount: (ReactComponentTreeHook: any).getUpdateCount(id), - childIDs: (ReactComponentTreeHook: any).getChildIDs(id), + displayName: getDisplayName && getDisplayName(id), + text: getText && getText(id), + updateCount: getUpdateCount && getUpdateCount(id), + childIDs: getChildIDs && getChildIDs(id), // Text nodes don't have owners but this is close enough. ownerID: ownerID || - parentID && (ReactComponentTreeHook: any).getOwnerID(parentID) || + parentID && getOwnerID && getOwnerID(parentID) || 0, parentID, }; @@ -144,13 +156,16 @@ if (__DEV__) { } if (previousMeasurements.length || previousOperations.length) { - var registeredIDs = (ReactComponentTreeHook: any).getRegisteredIDs(); - flushHistory.push({ - duration: performanceNow() - previousStartTime, - measurements: previousMeasurements || [], - operations: previousOperations || [], - treeSnapshot: getTreeSnapshot(registeredIDs), - }); + var registeredIDs = getRegisteredIDs && getRegisteredIDs(); + + if (registeredIDs) { + flushHistory.push({ + duration: performanceNow() - previousStartTime, + measurements: previousMeasurements || [], + operations: previousOperations || [], + treeSnapshot: getTreeSnapshot(registeredIDs), + }); + } } clearHistory(); @@ -280,7 +295,7 @@ if (__DEV__) { } var markName = `${debugID}::${markType}`; - var displayName = (ReactComponentTreeHook: any).getDisplayName(debugID) || 'Unknown'; + var displayName = getDisplayName && getDisplayName(debugID) || 'Unknown'; // Chrome has an issue of dropping markers recorded too fast: // https://bugs.chromium.org/p/chromium/issues/detail?id=640652 diff --git a/src/renderers/shared/stack/reconciler/ReactRef.js b/src/renderers/shared/stack/reconciler/ReactRef.js index 1a088e32da..155dab6c80 100644 --- a/src/renderers/shared/stack/reconciler/ReactRef.js +++ b/src/renderers/shared/stack/reconciler/ReactRef.js @@ -21,7 +21,9 @@ var ReactRef = {}; if (__DEV__) { var ReactCompositeComponentTypes = require('ReactCompositeComponentTypes'); - var ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook'); + var { + getStackAddendumByID, + } = require('react/lib/ReactComponentTreeHook'); var warning = require('fbjs/lib/warning'); var warnedAboutStatelessRefs = {}; @@ -53,7 +55,7 @@ function attachRef(ref, component, owner) { 'Stateless function components cannot be given refs. ' + 'Attempts to access this ref will fail.%s%s', info, - (ReactComponentTreeHook: any).getStackAddendumByID(component._debugID) + getStackAddendumByID && getStackAddendumByID(component._debugID) ); } } diff --git a/src/shared/utils/flattenChildren.js b/src/shared/utils/flattenChildren.js index 7bc7d7c94a..f2992becdf 100644 --- a/src/shared/utils/flattenChildren.js +++ b/src/shared/utils/flattenChildren.js @@ -51,6 +51,8 @@ function flattenSingleChildIntoContext( if (!ReactComponentTreeHook) { ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook'); } + const { getStackAddendumByID } = ReactComponentTreeHook; + if (!keyUnique) { warning( false, @@ -58,7 +60,7 @@ function flattenSingleChildIntoContext( '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), - (ReactComponentTreeHook: any).getStackAddendumByID(selfDebugID) + getStackAddendumByID && getStackAddendumByID(selfDebugID) ); } } From da9d91829e094fef634ce928b949512db3d07cdf Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 2 Mar 2017 21:50:02 +0000 Subject: [PATCH 03/12] WIP - not sure how to get the Flow type working properly --- flow/environment.js | 20 ------ flow/react-native-host-hooks.js | 64 ------------------- .../classic/element/ReactDebugCurrentFrame.js | 14 ++-- .../hooks/ReactComponentTreeHook.js | 24 ++++--- 4 files changed, 20 insertions(+), 102 deletions(-) delete mode 100644 flow/environment.js delete mode 100644 flow/react-native-host-hooks.js diff --git a/flow/environment.js b/flow/environment.js deleted file mode 100644 index fa020bf8aa..0000000000 --- a/flow/environment.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - */ - -/* eslint-disable */ - -declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: any; /*?{ - inject: ?((stuff: Object) => void) -};*/ - -// temporary patches for React.Component and React.Element -declare var ReactComponent: typeof React$Component; -declare var ReactElement: typeof React$Element; diff --git a/flow/react-native-host-hooks.js b/flow/react-native-host-hooks.js deleted file mode 100644 index 0f96747133..0000000000 --- a/flow/react-native-host-hooks.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - */ - -/* eslint-disable */ - -declare module 'deepDiffer' { - declare function exports(one: any, two: any): bool; -} -declare module 'deepFreezeAndThrowOnMutationInDev' { - declare function exports(obj : T) : T; -} -declare module 'flattenStyle' { } -declare module 'InitializeCore' { } -declare module 'RCTEventEmitter' { - declare function register() : void; -} -declare module 'TextInputState' { - declare function blurTextInput(object : any) : void; - declare function focusTextInput(object : any) : void; -} -declare module 'UIManager' { - declare var customBubblingEventTypes : Object; - declare var customDirectEventTypes : Object; - declare function createView( - reactTag : number, - viewName : string, - rootTag : number, - props : ?Object, - ) : void; - declare function manageChildren( - containerTag : number, - moveFromIndices : Array, - moveToIndices : Array, - addChildReactTags : Array, - addAtIndices : Array, - removeAtIndices : Array - ) : void; - declare function measure() : void; - declare function measureInWindow() : void; - declare function measureLayout() : void; - declare function removeRootView() : void; - declare function removeSubviewsFromContainerWithID() : void; - declare function replaceExistingNonRootView() : void; - declare function setChildren( - containerTag : number, - reactTags : Array, - ) : void; - declare function updateView( - reactTag : number, - viewName : string, - props : ?Object, - ) : void; -} -declare module 'View' { - declare var exports : typeof ReactComponent; -} diff --git a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js index 7a36e4f257..eb1d1ecaa4 100644 --- a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js +++ b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js @@ -14,16 +14,18 @@ import type { Fiber } from 'ReactFiber'; import type { DebugID } from 'ReactInstanceType'; -import type { ComponentTreeHookType } from '../../hooks/ReactComponentTreeHook'; +import type { ComponentTreeHookDevType } from '../../hooks/ReactComponentTreeHook'; const ReactDebugCurrentFrame = {}; if (__DEV__) { + // how do a state that ReactComponentTreeHook is using the ComponentTreeHookDevType type? + const ReactComponentTreeHook: ComponentTreeHookDevType = require('ReactComponentTreeHook'); const { getStackAddendumByID, getStackAddendumByWorkInProgressFiber, getCurrentStackAddendum, - }: ComponentTreeHookType = require('ReactComponentTreeHook'); + } = ReactComponentTreeHook; // Component that is being worked on ReactDebugCurrentFrame.current = (null : Fiber | DebugID | null); @@ -38,9 +40,7 @@ if (__DEV__) { if (typeof current === 'number') { // DebugID from Stack. const debugID = current; - if (getStackAddendumByID) { - stack = getStackAddendumByID(debugID); - } + stack = getStackAddendumByID(debugID); } else if (typeof current.tag === 'number') { // This is a Fiber. // The stack will only be correct if this is a work in progress @@ -49,9 +49,7 @@ if (__DEV__) { stack = getStackAddendumByWorkInProgressFiber(workInProgress); } } else if (element !== null) { - if (getCurrentStackAddendum) { - stack = getCurrentStackAddendum(element); - } + stack = getCurrentStackAddendum(element); } return stack; }; diff --git a/src/isomorphic/hooks/ReactComponentTreeHook.js b/src/isomorphic/hooks/ReactComponentTreeHook.js index a268651098..c7e2141b34 100644 --- a/src/isomorphic/hooks/ReactComponentTreeHook.js +++ b/src/isomorphic/hooks/ReactComponentTreeHook.js @@ -57,16 +57,20 @@ function describeFiber(fiber : Fiber) : string { export type ComponentTreeHookType = { getStackAddendumByWorkInProgressFiber: (Fiber) => any, - getStackAddendumByID?: () => any, - getCurrentStackAddendum?: () => any, - purgeUnmountedComponents?: () => any, - getOwnerID?: (DebugID) => any, - getParentID?: (DebugID) => any, - getDisplayName?: (DebugID) => any, - getText?: (DebugID) => any, - getUpdateCount?: (DebugID) => any, - getChildIDs?: (DebugID) => any, - getRegisteredIDs?: () => any, +}; + +export type ComponentTreeHookDevType = { + getStackAddendumByWorkInProgressFiber: (Fiber) => any, + getStackAddendumByID: () => any, + getCurrentStackAddendum: () => any, + purgeUnmountedComponents: () => any, + getOwnerID: (DebugID) => any, + getParentID: (DebugID) => any, + getDisplayName: (DebugID) => any, + getText: (DebugID) => any, + getUpdateCount: (DebugID) => any, + getChildIDs: (DebugID) => any, + getRegisteredIDs: () => any, }; var ReactComponentTreeHook: ComponentTreeHookType = { From 54fef501863eb2e53180eaaebdd10169dc4c3611 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 2 Mar 2017 21:52:54 +0000 Subject: [PATCH 04/12] re-added missing files --- flow/environment.js | 20 +++++++++++ flow/react-native-host-hooks.js | 64 +++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 flow/environment.js create mode 100644 flow/react-native-host-hooks.js diff --git a/flow/environment.js b/flow/environment.js new file mode 100644 index 0000000000..fa020bf8aa --- /dev/null +++ b/flow/environment.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ + +/* eslint-disable */ + +declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: any; /*?{ + inject: ?((stuff: Object) => void) +};*/ + +// temporary patches for React.Component and React.Element +declare var ReactComponent: typeof React$Component; +declare var ReactElement: typeof React$Element; diff --git a/flow/react-native-host-hooks.js b/flow/react-native-host-hooks.js new file mode 100644 index 0000000000..0f96747133 --- /dev/null +++ b/flow/react-native-host-hooks.js @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ + +/* eslint-disable */ + +declare module 'deepDiffer' { + declare function exports(one: any, two: any): bool; +} +declare module 'deepFreezeAndThrowOnMutationInDev' { + declare function exports(obj : T) : T; +} +declare module 'flattenStyle' { } +declare module 'InitializeCore' { } +declare module 'RCTEventEmitter' { + declare function register() : void; +} +declare module 'TextInputState' { + declare function blurTextInput(object : any) : void; + declare function focusTextInput(object : any) : void; +} +declare module 'UIManager' { + declare var customBubblingEventTypes : Object; + declare var customDirectEventTypes : Object; + declare function createView( + reactTag : number, + viewName : string, + rootTag : number, + props : ?Object, + ) : void; + declare function manageChildren( + containerTag : number, + moveFromIndices : Array, + moveToIndices : Array, + addChildReactTags : Array, + addAtIndices : Array, + removeAtIndices : Array + ) : void; + declare function measure() : void; + declare function measureInWindow() : void; + declare function measureLayout() : void; + declare function removeRootView() : void; + declare function removeSubviewsFromContainerWithID() : void; + declare function replaceExistingNonRootView() : void; + declare function setChildren( + containerTag : number, + reactTags : Array, + ) : void; + declare function updateView( + reactTag : number, + viewName : string, + props : ?Object, + ) : void; +} +declare module 'View' { + declare var exports : typeof ReactComponent; +} From 1f7cbb63e81fcdf9c81aee86478703f603575a8f Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 2 Mar 2017 22:06:46 +0000 Subject: [PATCH 05/12] updated flow annotations --- .../classic/element/ReactDebugCurrentFrame.js | 4 +- .../hooks/ReactComponentTreeHook.js | 1 + src/renderers/shared/ReactDebugTool.js | 44 +++++++++---------- .../shared/stack/reconciler/ReactRef.js | 5 ++- src/shared/utils/flattenChildren.js | 6 ++- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js index eb1d1ecaa4..e5e966b706 100644 --- a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js +++ b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js @@ -14,13 +14,13 @@ import type { Fiber } from 'ReactFiber'; import type { DebugID } from 'ReactInstanceType'; -import type { ComponentTreeHookDevType } from '../../hooks/ReactComponentTreeHook'; +import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook'; const ReactDebugCurrentFrame = {}; if (__DEV__) { // how do a state that ReactComponentTreeHook is using the ComponentTreeHookDevType type? - const ReactComponentTreeHook: ComponentTreeHookDevType = require('ReactComponentTreeHook'); + const ReactComponentTreeHook: ComponentTreeHookDevType = (require('ReactComponentTreeHook'): any); const { getStackAddendumByID, getStackAddendumByWorkInProgressFiber, diff --git a/src/isomorphic/hooks/ReactComponentTreeHook.js b/src/isomorphic/hooks/ReactComponentTreeHook.js index c7e2141b34..5d8cdf616d 100644 --- a/src/isomorphic/hooks/ReactComponentTreeHook.js +++ b/src/isomorphic/hooks/ReactComponentTreeHook.js @@ -71,6 +71,7 @@ export type ComponentTreeHookDevType = { getUpdateCount: (DebugID) => any, getChildIDs: (DebugID) => any, getRegisteredIDs: () => any, + getElement: () => any, }; var ReactComponentTreeHook: ComponentTreeHookType = { diff --git a/src/renderers/shared/ReactDebugTool.js b/src/renderers/shared/ReactDebugTool.js index 1d07610ade..b0e91811e0 100644 --- a/src/renderers/shared/ReactDebugTool.js +++ b/src/renderers/shared/ReactDebugTool.js @@ -22,6 +22,7 @@ var warning = require('fbjs/lib/warning'); import type { ReactElement } from 'ReactElementType'; import type { DebugID } from 'ReactInstanceType'; import type { Operation } from 'ReactHostOperationHistoryHook'; +import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook'; type Hook = any; @@ -68,7 +69,7 @@ var ReactDebugTool = ((null: any): typeof ReactDebugTool); if (__DEV__) { const hooks = []; const didHookThrowForEvent = {}; - const ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook'); + const ReactComponentTreeHook: ComponentTreeHookDevType = (require('react/lib/ReactComponentTreeHook'): any); const { purgeUnmountedComponents, getOwnerID, @@ -78,6 +79,7 @@ if (__DEV__) { getUpdateCount, getChildIDs, getRegisteredIDs, + getElement, } = ReactComponentTreeHook; const callHook = function(event, fn, context, arg1, arg2, arg3, arg4, arg5) { @@ -118,24 +120,22 @@ if (__DEV__) { var lifeCycleTimerHasWarned = false; const clearHistory = function() { - if (purgeUnmountedComponents) { - purgeUnmountedComponents(); - } + purgeUnmountedComponents(); ReactHostOperationHistoryHook.clearHistory(); }; const getTreeSnapshot = function(registeredIDs) { - return registeredIDs && registeredIDs.reduce((tree, id) => { - var ownerID = getOwnerID && getOwnerID(id); - var parentID = getParentID && getParentID(id); + return registeredIDs.reduce((tree, id) => { + var ownerID = getOwnerID(id); + var parentID = getParentID(id); tree[id] = { - displayName: getDisplayName && getDisplayName(id), - text: getText && getText(id), - updateCount: getUpdateCount && getUpdateCount(id), - childIDs: getChildIDs && getChildIDs(id), + displayName: getDisplayName(id), + text: getText(id), + updateCount: getUpdateCount(id), + childIDs: getChildIDs(id), // Text nodes don't have owners but this is close enough. ownerID: ownerID || - parentID && getOwnerID && getOwnerID(parentID) || + parentID && getOwnerID(parentID) || 0, parentID, }; @@ -156,16 +156,14 @@ if (__DEV__) { } if (previousMeasurements.length || previousOperations.length) { - var registeredIDs = getRegisteredIDs && getRegisteredIDs(); + var registeredIDs = getRegisteredIDs(); - if (registeredIDs) { - flushHistory.push({ - duration: performanceNow() - previousStartTime, - measurements: previousMeasurements || [], - operations: previousOperations || [], - treeSnapshot: getTreeSnapshot(registeredIDs), - }); - } + flushHistory.push({ + duration: performanceNow() - previousStartTime, + measurements: previousMeasurements || [], + operations: previousOperations || [], + treeSnapshot: getTreeSnapshot(registeredIDs), + }); } clearHistory(); @@ -268,7 +266,7 @@ if (__DEV__) { if (!isProfiling || !canUsePerformanceMeasure) { return false; } - var element = (ReactComponentTreeHook: any).getElement(debugID); + var element = getElement(debugID); if (element == null || typeof element !== 'object') { return false; } @@ -295,7 +293,7 @@ if (__DEV__) { } var markName = `${debugID}::${markType}`; - var displayName = getDisplayName && getDisplayName(debugID) || 'Unknown'; + var displayName = getDisplayName(debugID) || 'Unknown'; // Chrome has an issue of dropping markers recorded too fast: // https://bugs.chromium.org/p/chromium/issues/detail?id=640652 diff --git a/src/renderers/shared/stack/reconciler/ReactRef.js b/src/renderers/shared/stack/reconciler/ReactRef.js index 155dab6c80..7f2e5c4567 100644 --- a/src/renderers/shared/stack/reconciler/ReactRef.js +++ b/src/renderers/shared/stack/reconciler/ReactRef.js @@ -16,6 +16,7 @@ var ReactOwner = require('ReactOwner'); import type { ReactInstance } from 'ReactInstanceType'; import type { ReactElement } from 'ReactElementType'; +import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook'; var ReactRef = {}; @@ -23,7 +24,7 @@ if (__DEV__) { var ReactCompositeComponentTypes = require('ReactCompositeComponentTypes'); var { getStackAddendumByID, - } = require('react/lib/ReactComponentTreeHook'); + }: ComponentTreeHookDevType = (require('react/lib/ReactComponentTreeHook'): any); var warning = require('fbjs/lib/warning'); var warnedAboutStatelessRefs = {}; @@ -55,7 +56,7 @@ function attachRef(ref, component, owner) { 'Stateless function components cannot be given refs. ' + 'Attempts to access this ref will fail.%s%s', info, - getStackAddendumByID && getStackAddendumByID(component._debugID) + getStackAddendumByID(component._debugID) ); } } diff --git a/src/shared/utils/flattenChildren.js b/src/shared/utils/flattenChildren.js index f2992becdf..2a8165c829 100644 --- a/src/shared/utils/flattenChildren.js +++ b/src/shared/utils/flattenChildren.js @@ -12,6 +12,8 @@ 'use strict'; +import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook'; + var KeyEscapeUtils = require('KeyEscapeUtils'); var traverseAllChildren = require('traverseAllChildren'); var warning = require('fbjs/lib/warning'); @@ -51,7 +53,7 @@ function flattenSingleChildIntoContext( if (!ReactComponentTreeHook) { ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook'); } - const { getStackAddendumByID } = ReactComponentTreeHook; + const { getStackAddendumByID }: ComponentTreeHookDevType = (ReactComponentTreeHook: any); if (!keyUnique) { warning( @@ -60,7 +62,7 @@ function flattenSingleChildIntoContext( '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), - getStackAddendumByID && getStackAddendumByID(selfDebugID) + getStackAddendumByID(selfDebugID) ); } } From 9a1db4925968af23570e8e8ae84234b7f0eb452c Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 2 Mar 2017 22:08:15 +0000 Subject: [PATCH 06/12] removed comment --- src/isomorphic/classic/element/ReactDebugCurrentFrame.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js index e5e966b706..6bb331994d 100644 --- a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js +++ b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js @@ -19,13 +19,11 @@ import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook'; const ReactDebugCurrentFrame = {}; if (__DEV__) { - // how do a state that ReactComponentTreeHook is using the ComponentTreeHookDevType type? - const ReactComponentTreeHook: ComponentTreeHookDevType = (require('ReactComponentTreeHook'): any); const { getStackAddendumByID, getStackAddendumByWorkInProgressFiber, getCurrentStackAddendum, - } = ReactComponentTreeHook; + }: ComponentTreeHookDevType = (require('ReactComponentTreeHook'): any);; // Component that is being worked on ReactDebugCurrentFrame.current = (null : Fiber | DebugID | null); From 397fd2b92ec8dc5b1eb8dca097592d376e6b40e1 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 2 Mar 2017 22:09:12 +0000 Subject: [PATCH 07/12] removed double semicolon --- src/isomorphic/classic/element/ReactDebugCurrentFrame.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js index 6bb331994d..9e90f13f9f 100644 --- a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js +++ b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js @@ -23,7 +23,7 @@ if (__DEV__) { getStackAddendumByID, getStackAddendumByWorkInProgressFiber, getCurrentStackAddendum, - }: ComponentTreeHookDevType = (require('ReactComponentTreeHook'): any);; + }: ComponentTreeHookDevType = (require('ReactComponentTreeHook'): any); // Component that is being worked on ReactDebugCurrentFrame.current = (null : Fiber | DebugID | null); From 91de17ec5212c4fc16c01c66db3645f52901c1f9 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 2 Mar 2017 23:06:32 +0000 Subject: [PATCH 08/12] added guards in place to prevent mixed prod+dev errors from occuring --- .../classic/element/ReactDebugCurrentFrame.js | 4 ++-- src/renderers/shared/ReactDebugTool.js | 24 +++++++++++-------- .../shared/stack/reconciler/ReactRef.js | 2 +- src/shared/utils/flattenChildren.js | 2 +- src/umd/ReactUMDEntry.js | 11 +-------- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js index 9e90f13f9f..056a38bbcb 100644 --- a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js +++ b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js @@ -38,7 +38,7 @@ if (__DEV__) { if (typeof current === 'number') { // DebugID from Stack. const debugID = current; - stack = getStackAddendumByID(debugID); + stack = getStackAddendumByID && getStackAddendumByID(debugID); } else if (typeof current.tag === 'number') { // This is a Fiber. // The stack will only be correct if this is a work in progress @@ -47,7 +47,7 @@ if (__DEV__) { stack = getStackAddendumByWorkInProgressFiber(workInProgress); } } else if (element !== null) { - stack = getCurrentStackAddendum(element); + stack = getCurrentStackAddendum && getCurrentStackAddendum(element); } return stack; }; diff --git a/src/renderers/shared/ReactDebugTool.js b/src/renderers/shared/ReactDebugTool.js index b0e91811e0..b8a3ae7e6e 100644 --- a/src/renderers/shared/ReactDebugTool.js +++ b/src/renderers/shared/ReactDebugTool.js @@ -120,7 +120,9 @@ if (__DEV__) { var lifeCycleTimerHasWarned = false; const clearHistory = function() { - purgeUnmountedComponents(); + if (purgeUnmountedComponents) { + purgeUnmountedComponents(); + } ReactHostOperationHistoryHook.clearHistory(); }; @@ -156,14 +158,16 @@ if (__DEV__) { } if (previousMeasurements.length || previousOperations.length) { - var registeredIDs = getRegisteredIDs(); + if (getRegisteredIDs) { + var registeredIDs = getRegisteredIDs(); - flushHistory.push({ - duration: performanceNow() - previousStartTime, - measurements: previousMeasurements || [], - operations: previousOperations || [], - treeSnapshot: getTreeSnapshot(registeredIDs), - }); + flushHistory.push({ + duration: performanceNow() - previousStartTime, + measurements: previousMeasurements || [], + operations: previousOperations || [], + treeSnapshot: getTreeSnapshot(registeredIDs), + }); + } } clearHistory(); @@ -266,7 +270,7 @@ if (__DEV__) { if (!isProfiling || !canUsePerformanceMeasure) { return false; } - var element = getElement(debugID); + var element = getElement && getElement(debugID); if (element == null || typeof element !== 'object') { return false; } @@ -293,7 +297,7 @@ if (__DEV__) { } var markName = `${debugID}::${markType}`; - var displayName = getDisplayName(debugID) || 'Unknown'; + var displayName = getDisplayName && getDisplayName(debugID) || 'Unknown'; // Chrome has an issue of dropping markers recorded too fast: // https://bugs.chromium.org/p/chromium/issues/detail?id=640652 diff --git a/src/renderers/shared/stack/reconciler/ReactRef.js b/src/renderers/shared/stack/reconciler/ReactRef.js index 7f2e5c4567..6705ec9aa3 100644 --- a/src/renderers/shared/stack/reconciler/ReactRef.js +++ b/src/renderers/shared/stack/reconciler/ReactRef.js @@ -49,7 +49,7 @@ function attachRef(ref, component, owner) { if (element && element._source) { warningKey = element._source.fileName + ':' + element._source.lineNumber; } - if (!warnedAboutStatelessRefs[warningKey]) { + if (!warnedAboutStatelessRefs[warningKey] && getStackAddendumByID) { warnedAboutStatelessRefs[warningKey] = true; warning( false, diff --git a/src/shared/utils/flattenChildren.js b/src/shared/utils/flattenChildren.js index 2a8165c829..a5a3d85bb5 100644 --- a/src/shared/utils/flattenChildren.js +++ b/src/shared/utils/flattenChildren.js @@ -55,7 +55,7 @@ function flattenSingleChildIntoContext( } const { getStackAddendumByID }: ComponentTreeHookDevType = (ReactComponentTreeHook: any); - if (!keyUnique) { + if (!keyUnique && getStackAddendumByID) { warning( false, 'flattenChildren(...): Encountered two children with the same key, ' + diff --git a/src/umd/ReactUMDEntry.js b/src/umd/ReactUMDEntry.js index ab926354a6..8cacdceb35 100644 --- a/src/umd/ReactUMDEntry.js +++ b/src/umd/ReactUMDEntry.js @@ -17,17 +17,8 @@ var React = require('React'); var ReactUMDEntry = Object.assign({ __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { ReactCurrentOwner: require('react/lib/ReactCurrentOwner'), + ReactComponentTreeHook: require('react/lib/ReactComponentTreeHook'), }, }, React); -if (__DEV__) { - Object.assign( - ReactUMDEntry.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, - { - // ReactComponentTreeHook should not be included in production. - ReactComponentTreeHook: require('react/lib/ReactComponentTreeHook'), - } - ); -} - module.exports = ReactUMDEntry; From b572e3c907e31700b140d750fd77977f0fd15885 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 6 Mar 2017 14:19:23 +0000 Subject: [PATCH 09/12] 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 --- .../classic/element/ReactDebugCurrentFrame.js | 14 +- .../classic/element/ReactElementValidator.js | 8 +- .../hooks/ReactComponentTreeHook.js | 773 ++++++++---------- .../shared/hooks/ReactDOMInvalidARIAHook.js | 9 +- .../hooks/ReactDOMNullInputValuePropHook.js | 10 +- src/renderers/shared/ReactDebugTool.js | 57 +- .../shared/fiber/ReactDebugCurrentFiber.js | 2 +- .../shared/fiber/ReactFiberScheduler.js | 2 +- .../shared/stack/reconciler/ReactRef.js | 9 +- src/shared/ReactFiberComponentTreeHook.js | 77 ++ src/shared/utils/flattenChildren.js | 8 +- src/umd/ReactUMDEntry.js | 11 +- 12 files changed, 486 insertions(+), 494 deletions(-) create mode 100644 src/shared/ReactFiberComponentTreeHook.js diff --git a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js index 056a38bbcb..ee8ab2248e 100644 --- a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js +++ b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js @@ -14,16 +14,18 @@ import type { Fiber } from 'ReactFiber'; import type { DebugID } from 'ReactInstanceType'; -import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook'; const ReactDebugCurrentFrame = {}; if (__DEV__) { - const { + var { getStackAddendumByID, - getStackAddendumByWorkInProgressFiber, getCurrentStackAddendum, - }: ComponentTreeHookDevType = (require('ReactComponentTreeHook'): any); + } = require('ReactComponentTreeHook'); + var { + getStackAddendumByWorkInProgressFiber, + } = require('ReactFiberComponentTreeHook'); + // Component that is being worked on ReactDebugCurrentFrame.current = (null : Fiber | DebugID | null); @@ -38,7 +40,7 @@ if (__DEV__) { if (typeof current === 'number') { // DebugID from Stack. const debugID = current; - stack = getStackAddendumByID && getStackAddendumByID(debugID); + stack = getStackAddendumByID(debugID); } else if (typeof current.tag === 'number') { // This is a Fiber. // The stack will only be correct if this is a work in progress @@ -47,7 +49,7 @@ if (__DEV__) { stack = getStackAddendumByWorkInProgressFiber(workInProgress); } } else if (element !== null) { - stack = getCurrentStackAddendum && getCurrentStackAddendum(element); + stack = getCurrentStackAddendum(element); } return stack; }; diff --git a/src/isomorphic/classic/element/ReactElementValidator.js b/src/isomorphic/classic/element/ReactElementValidator.js index b70fedc37e..7f30253349 100644 --- a/src/isomorphic/classic/element/ReactElementValidator.js +++ b/src/isomorphic/classic/element/ReactElementValidator.js @@ -19,7 +19,6 @@ 'use strict'; var ReactCurrentOwner = require('ReactCurrentOwner'); -var ReactComponentTreeHook = require('ReactComponentTreeHook'); var ReactElement = require('ReactElement'); var checkReactTypeSpec = require('checkReactTypeSpec'); @@ -31,6 +30,9 @@ var getIteratorFn = require('getIteratorFn'); if (__DEV__) { var warning = require('fbjs/lib/warning'); var ReactDebugCurrentFrame = require('ReactDebugCurrentFrame'); + var { + getCurrentStackAddendum, + } = require('ReactComponentTreeHook'); } function getDeclarationErrorAddendum() { @@ -122,7 +124,7 @@ function validateExplicitKey(element, parentType) { '%s%s See https://fb.me/react-warning-keys for more information.%s', currentComponentErrorInfo, childOwner, - ReactComponentTreeHook.getCurrentStackAddendum(element) + getCurrentStackAddendum && getCurrentStackAddendum(element) ); } @@ -225,7 +227,7 @@ var ReactElementValidator = { info += getDeclarationErrorAddendum(); } - info += ReactComponentTreeHook.getCurrentStackAddendum(); + info += getCurrentStackAddendum && getCurrentStackAddendum(); warning( false, diff --git a/src/isomorphic/hooks/ReactComponentTreeHook.js b/src/isomorphic/hooks/ReactComponentTreeHook.js index 5d8cdf616d..9fede1ac22 100644 --- a/src/isomorphic/hooks/ReactComponentTreeHook.js +++ b/src/isomorphic/hooks/ReactComponentTreeHook.js @@ -12,476 +12,393 @@ 'use strict'; +var ReactCurrentOwner = require('ReactCurrentOwner'); +var { + getStackAddendumByWorkInProgressFiber, + describeComponentFrame, +} = require('ReactFiberComponentTreeHook'); +var invariant = require('fbjs/lib/invariant'); +var warning = require('fbjs/lib/warning'); +var getComponentName = require('getComponentName'); + import type { ReactElement, Source } from 'ReactElementType'; import type { DebugID } from 'ReactInstanceType'; import type { Fiber } from 'ReactFiber'; -var getComponentName = require('getComponentName'); -var ReactTypeOfWork = require('ReactTypeOfWork'); -var { - IndeterminateComponent, - FunctionalComponent, - ClassComponent, - HostComponent, -} = ReactTypeOfWork; - -function describeComponentFrame(name, source, ownerName) { - return '\n in ' + (name || 'Unknown') + ( - source ? - ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + - source.lineNumber + ')' : - ownerName ? - ' (created by ' + ownerName + ')' : - '' +function isNative(fn) { + // Based on isNative() from Lodash + var funcToString = Function.prototype.toString; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var reIsNative = RegExp('^' + funcToString + // Take an example native function source for comparison + .call(hasOwnProperty) + // Strip regex characters so we can use it for regex + .replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + // Remove hasOwnProperty from the template to make it generic + .replace( + /hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, + '$1.*?' + ) + '$' ); -} - -function describeFiber(fiber : Fiber) : string { - switch (fiber.tag) { - case IndeterminateComponent: - case FunctionalComponent: - case ClassComponent: - case HostComponent: - var owner = fiber._debugOwner; - var source = fiber._debugSource; - var name = getComponentName(fiber); - var ownerName = null; - if (owner) { - ownerName = getComponentName(owner); - } - return describeComponentFrame(name, source, ownerName); - default: - return ''; + try { + var source = funcToString.call(fn); + return reIsNative.test(source); + } catch (err) { + return false; } } -export type ComponentTreeHookType = { - getStackAddendumByWorkInProgressFiber: (Fiber) => any, -}; +var canUseCollections = ( + // Array.from + typeof Array.from === 'function' && + // Map + typeof Map === 'function' && + isNative(Map) && + // Map.prototype.keys + Map.prototype != null && + typeof Map.prototype.keys === 'function' && + isNative(Map.prototype.keys) && + // Set + typeof Set === 'function' && + isNative(Set) && + // Set.prototype.keys + Set.prototype != null && + typeof Set.prototype.keys === 'function' && + isNative(Set.prototype.keys) +); -export type ComponentTreeHookDevType = { - getStackAddendumByWorkInProgressFiber: (Fiber) => any, - getStackAddendumByID: () => any, - getCurrentStackAddendum: () => any, - purgeUnmountedComponents: () => any, - getOwnerID: (DebugID) => any, - getParentID: (DebugID) => any, - getDisplayName: (DebugID) => any, - getText: (DebugID) => any, - getUpdateCount: (DebugID) => any, - getChildIDs: (DebugID) => any, - getRegisteredIDs: () => any, - getElement: () => any, -}; +var setItem; +var getItem; +var removeItem; +var getItemIDs; +var addRoot; +var removeRoot; +var getRootIDs; -var ReactComponentTreeHook: ComponentTreeHookType = { - // This function can only be called with a work-in-progress fiber and - // only during begin or complete phase. Do not call it under any other - // circumstances. - getStackAddendumByWorkInProgressFiber(workInProgress : Fiber) : string { - var info = ''; - var node = workInProgress; - do { - info += describeFiber(node); - // Otherwise this return pointer might point to the wrong tree: - node = node.return; - } while (node); - return info; - }, -}; +if (canUseCollections) { + var itemMap = new Map(); + var rootIDSet = new Set(); -if (__DEV__) { - var ReactCurrentOwner = require('ReactCurrentOwner'); - var invariant = require('fbjs/lib/invariant'); - var warning = require('fbjs/lib/warning'); - - var isNative = function(fn) { - // Based on isNative() from Lodash - var funcToString = Function.prototype.toString; - var hasOwnProperty = Object.prototype.hasOwnProperty; - var reIsNative = RegExp('^' + funcToString - // Take an example native function source for comparison - .call(hasOwnProperty) - // Strip regex characters so we can use it for regex - .replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') - // Remove hasOwnProperty from the template to make it generic - .replace( - /hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, - '$1.*?' - ) + '$' - ); - try { - var source = funcToString.call(fn); - return reIsNative.test(source); - } catch (err) { - return false; - } + setItem = function(id, item) { + itemMap.set(id, item); + }; + getItem = function(id) { + return itemMap.get(id); + }; + removeItem = function(id) { + itemMap.delete(id); + }; + getItemIDs = function() { + return Array.from(itemMap.keys()); }; - var canUseCollections = ( - // Array.from - typeof Array.from === 'function' && - // Map - typeof Map === 'function' && - isNative(Map) && - // Map.prototype.keys - Map.prototype != null && - typeof Map.prototype.keys === 'function' && - isNative(Map.prototype.keys) && - // Set - typeof Set === 'function' && - isNative(Set) && - // Set.prototype.keys - Set.prototype != null && - typeof Set.prototype.keys === 'function' && - isNative(Set.prototype.keys) - ); + addRoot = function(id) { + rootIDSet.add(id); + }; + removeRoot = function(id) { + rootIDSet.delete(id); + }; + getRootIDs = function() { + return Array.from(rootIDSet.keys()); + }; - var setItem; - var getItem; - var removeItem; - var getItemIDs; - var addRoot; - var removeRoot; - var getRootIDs; +} else { + var itemByKey = {}; + var rootByKey = {}; - if (canUseCollections) { - var itemMap = new Map(); - var rootIDSet = new Set(); + // Use non-numeric keys to prevent V8 performance issues: + // https://github.com/facebook/react/pull/7232 + var getKeyFromID = function(id: DebugID): string { + return '.' + id; + }; + var getIDFromKey = function(key: string): DebugID { + return parseInt(key.substr(1), 10); + }; - setItem = function(id, item) { - itemMap.set(id, item); - }; - getItem = function(id) { - return itemMap.get(id); - }; - removeItem = function(id) { - itemMap.delete(id); - }; - getItemIDs = function() { - return Array.from(itemMap.keys()); - }; + setItem = function(id, item) { + var key = getKeyFromID(id); + itemByKey[key] = item; + }; + getItem = function(id) { + var key = getKeyFromID(id); + return itemByKey[key]; + }; + removeItem = function(id) { + var key = getKeyFromID(id); + delete itemByKey[key]; + }; + getItemIDs = function() { + return Object.keys(itemByKey).map(getIDFromKey); + }; - addRoot = function(id) { - rootIDSet.add(id); - }; - removeRoot = function(id) { - rootIDSet.delete(id); - }; - getRootIDs = function() { - return Array.from(rootIDSet.keys()); - }; + addRoot = function(id) { + var key = getKeyFromID(id); + rootByKey[key] = true; + }; + removeRoot = function(id) { + var key = getKeyFromID(id); + delete rootByKey[key]; + }; + getRootIDs = function() { + return Object.keys(rootByKey).map(getIDFromKey); + }; +} - } else { - var itemByKey = {}; - var rootByKey = {}; +var unmountedIDs: Array = []; - // Use non-numeric keys to prevent V8 performance issues: - // https://github.com/facebook/react/pull/7232 - var getKeyFromID = function(id: DebugID): string { - return '.' + id; - }; - var getIDFromKey = function(key: string): DebugID { - return parseInt(key.substr(1), 10); - }; - - setItem = function(id, item) { - var key = getKeyFromID(id); - itemByKey[key] = item; - }; - getItem = function(id) { - var key = getKeyFromID(id); - return itemByKey[key]; - }; - removeItem = function(id) { - var key = getKeyFromID(id); - delete itemByKey[key]; - }; - getItemIDs = function() { - return Object.keys(itemByKey).map(getIDFromKey); - }; - - addRoot = function(id) { - var key = getKeyFromID(id); - rootByKey[key] = true; - }; - removeRoot = function(id) { - var key = getKeyFromID(id); - delete rootByKey[key]; - }; - getRootIDs = function() { - return Object.keys(rootByKey).map(getIDFromKey); - }; +function purgeDeep(id) { + var item = getItem(id); + if (item) { + var {childIDs} = item; + removeItem(id); + childIDs.forEach(purgeDeep); } +} - const unmountedIDs: Array = []; +function getDisplayName(element: ?ReactElement): string { + if (element == null) { + return '#empty'; + } else if (typeof element === 'string' || typeof element === 'number') { + return '#text'; + } else if (typeof element.type === 'string') { + return element.type; + } else { + return element.type.displayName || element.type.name || 'Unknown'; + } +} - const purgeDeep = function(id) { +function describeID(id: DebugID): string { + const name = ReactComponentTreeHook.getDisplayName(id); + const element = ReactComponentTreeHook.getElement(id); + const ownerID = ReactComponentTreeHook.getOwnerID(id); + let ownerName; + + if (ownerID) { + ownerName = ReactComponentTreeHook.getDisplayName(ownerID); + } + warning( + element, + 'ReactComponentTreeHook: Missing React element for debugID %s when ' + + 'building stack', + id + ); + if (element && name) { + return describeComponentFrame(name || '', element._source, ownerName || ''); + } + return ''; +} + +var ReactComponentTreeHook = { + onSetChildren(id: DebugID, nextChildIDs: Array): void { + var item = getItem(id); + invariant(item, 'Item must have been set'); + item.childIDs = nextChildIDs; + + for (var i = 0; i < nextChildIDs.length; i++) { + var nextChildID = nextChildIDs[i]; + var nextChild = getItem(nextChildID); + invariant( + nextChild, + 'Expected hook events to fire for the child ' + + 'before its parent includes it in onSetChildren().' + ); + invariant( + nextChild.childIDs != null || + typeof nextChild.element !== 'object' || + nextChild.element == null, + 'Expected onSetChildren() to fire for a container child ' + + 'before its parent includes it in onSetChildren().' + ); + invariant( + nextChild.isMounted, + 'Expected onMountComponent() to fire for the child ' + + 'before its parent includes it in onSetChildren().' + ); + if (nextChild.parentID == null) { + nextChild.parentID = id; + // TODO: This shouldn't be necessary but mounting a new root during in + // componentWillMount currently causes not-yet-mounted components to + // be purged from our tree data so their parent id is missing. + } + invariant( + nextChild.parentID === id, + 'Expected onBeforeMountComponent() parent and onSetChildren() to ' + + 'be consistent (%s has parents %s and %s).', + nextChildID, + nextChild.parentID, + id + ); + } + }, + + onBeforeMountComponent(id: DebugID, element: ReactElement, parentID: DebugID): void { + var item = { + element, + parentID, + text: null, + childIDs: [], + isMounted: false, + updateCount: 0, + }; + setItem(id, item); + }, + + onBeforeUpdateComponent(id: DebugID, element: ReactElement): void { + var item = getItem(id); + if (!item || !item.isMounted) { + // We may end up here as a result of setState() in componentWillUnmount(). + // In this case, ignore the element. + return; + } + item.element = element; + }, + + onMountComponent(id: DebugID): void { + var item = getItem(id); + invariant(item, 'Item must have been set'); + item.isMounted = true; + var isRoot = item.parentID === 0; + if (isRoot) { + addRoot(id); + } + }, + + onUpdateComponent(id: DebugID): void { + var item = getItem(id); + if (!item || !item.isMounted) { + // We may end up here as a result of setState() in componentWillUnmount(). + // In this case, ignore the element. + return; + } + item.updateCount++; + }, + + onUnmountComponent(id: DebugID): void { var item = getItem(id); if (item) { - var {childIDs} = item; - removeItem(id); - childIDs.forEach(purgeDeep); + // We need to check if it exists. + // `item` might not exist if it is inside an error boundary, and a sibling + // error boundary child threw while mounting. Then this instance never + // got a chance to mount, but it still gets an unmounting event during + // the error boundary cleanup. + item.isMounted = false; + var isRoot = item.parentID === 0; + if (isRoot) { + removeRoot(id); + } } - }; + unmountedIDs.push(id); + }, - const getDisplayNameFromElement = function(element: ?ReactElement): string { - if (element == null) { - return '#empty'; - } else if (typeof element === 'string' || typeof element === 'number') { - return '#text'; - } else if (typeof element.type === 'string') { - return element.type; - } else { - return element.type.displayName || element.type.name || 'Unknown'; + purgeUnmountedComponents(): void { + if (ReactComponentTreeHook._preventPurging) { + // Should only be used for testing. + return; } - }; - const getDisplayName = function(id: DebugID): ?string { - var element = getElement(id); + for (var i = 0; i < unmountedIDs.length; i++) { + var id = unmountedIDs[i]; + purgeDeep(id); + } + unmountedIDs.length = 0; + }, + + isMounted(id: DebugID): boolean { + var item = getItem(id); + return item ? item.isMounted : false; + }, + + getCurrentStackAddendum(topElement: ?ReactElement): string { + var info = ''; + if (topElement) { + var name = getDisplayName(topElement); + var owner = topElement._owner; + info += describeComponentFrame( + name, + topElement._source, + owner && getComponentName(owner) + ); + } + + var currentOwner = ReactCurrentOwner.current; + if (currentOwner) { + if (typeof currentOwner.tag === 'number') { + const workInProgress = ((currentOwner : any) : Fiber); + // Safe because if current owner exists, we are reconciling, + // and it is guaranteed to be the work-in-progress version. + info += getStackAddendumByWorkInProgressFiber(workInProgress); + } else if (typeof currentOwner._debugID === 'number') { + info += ReactComponentTreeHook.getStackAddendumByID(currentOwner._debugID); + } + } + return info; + }, + + getStackAddendumByID(id: ?DebugID): string { + var info = ''; + while (id) { + info += describeID(id); + id = ReactComponentTreeHook.getParentID(id); + } + return info; + }, + + getChildIDs(id: DebugID): Array { + var item = getItem(id); + return item ? item.childIDs : []; + }, + + getDisplayName(id: DebugID): ?string { + var element = ReactComponentTreeHook.getElement(id); if (!element) { return null; } - return getDisplayNameFromElement(element); - }; + return getDisplayName(element); + }, - const getOwnerID = function(id: DebugID): ?DebugID { - var element = getElement(id); + getElement(id: DebugID): ?ReactElement { + var item = getItem(id); + return item ? item.element : null; + }, + + getOwnerID(id: DebugID): ?DebugID { + var element = ReactComponentTreeHook.getElement(id); if (!element || !element._owner) { return null; } return element._owner._debugID; - }; + }, - const describeID = function(id: DebugID): string { - var name = getDisplayName(id); - var element = getElement(id); - var ownerID = getOwnerID(id); - var ownerName; - if (ownerID) { - ownerName = getDisplayName(ownerID); - } - warning( - element, - 'ReactComponentTreeHook: Missing React element for debugID %s when ' + - 'building stack', - id - ); - return describeComponentFrame(name, element && element._source, ownerName); - }; - - const getElement = function(id: DebugID): ?ReactElement { - var item = getItem(id); - return item ? item.element : null; - }; - - const getParentID = function(id: DebugID): ?DebugID { + getParentID(id: DebugID): ?DebugID { var item = getItem(id); return item ? item.parentID : null; - }; + }, - const getStackAddendumByID = function(id: ?DebugID): string { - var info = ''; - while (id) { - info += describeID(id); - id = getParentID(id); + getSource(id: DebugID): ?Source { + var item = getItem(id); + var element = item ? item.element : null; + var source = element != null ? element._source : null; + return source; + }, + + getText(id: DebugID): ?string { + var element = ReactComponentTreeHook.getElement(id); + if (typeof element === 'string') { + return element; + } else if (typeof element === 'number') { + return '' + element; + } else { + return null; } - return info; - }; + }, - ReactComponentTreeHook = Object.assign({}, ReactComponentTreeHook, { - onSetChildren(id: DebugID, nextChildIDs: Array): void { - var item = getItem(id); - invariant(item, 'Item must have been set'); - item.childIDs = nextChildIDs; + getUpdateCount(id: DebugID): number { + var item = getItem(id); + return item ? item.updateCount : 0; + }, - for (var i = 0; i < nextChildIDs.length; i++) { - var nextChildID = nextChildIDs[i]; - var nextChild = getItem(nextChildID); - invariant( - nextChild, - 'Expected hook events to fire for the child ' + - 'before its parent includes it in onSetChildren().' - ); - invariant( - nextChild.childIDs != null || - typeof nextChild.element !== 'object' || - nextChild.element == null, - 'Expected onSetChildren() to fire for a container child ' + - 'before its parent includes it in onSetChildren().' - ); - invariant( - nextChild.isMounted, - 'Expected onMountComponent() to fire for the child ' + - 'before its parent includes it in onSetChildren().' - ); - if (nextChild.parentID == null) { - nextChild.parentID = id; - // TODO: This shouldn't be necessary but mounting a new root during in - // componentWillMount currently causes not-yet-mounted components to - // be purged from our tree data so their parent id is missing. - } - invariant( - nextChild.parentID === id, - 'Expected onBeforeMountComponent() parent and onSetChildren() to ' + - 'be consistent (%s has parents %s and %s).', - nextChildID, - nextChild.parentID, - id - ); - } - }, - - onBeforeMountComponent(id: DebugID, element: ReactElement, parentID: DebugID): void { - var item = { - element, - parentID, - text: null, - childIDs: [], - isMounted: false, - updateCount: 0, - }; - setItem(id, item); - }, - - onBeforeUpdateComponent(id: DebugID, element: ReactElement): void { - var item = getItem(id); - if (!item || !item.isMounted) { - // We may end up here as a result of setState() in componentWillUnmount(). - // In this case, ignore the element. - return; - } - item.element = element; - }, - - onMountComponent(id: DebugID): void { - var item = getItem(id); - invariant(item, 'Item must have been set'); - item.isMounted = true; - var isRoot = item.parentID === 0; - if (isRoot) { - addRoot(id); - } - }, - - onUpdateComponent(id: DebugID): void { - var item = getItem(id); - if (!item || !item.isMounted) { - // We may end up here as a result of setState() in componentWillUnmount(). - // In this case, ignore the element. - return; - } - item.updateCount++; - }, - - onUnmountComponent(id: DebugID): void { - var item = getItem(id); - if (item) { - // We need to check if it exists. - // `item` might not exist if it is inside an error boundary, and a sibling - // error boundary child threw while mounting. Then this instance never - // got a chance to mount, but it still gets an unmounting event during - // the error boundary cleanup. - item.isMounted = false; - var isRoot = item.parentID === 0; - if (isRoot) { - removeRoot(id); - } - } - unmountedIDs.push(id); - }, - - purgeUnmountedComponents(): void { - if (ReactComponentTreeHook._preventPurging) { - // Should only be used for testing. - return; - } - - for (var i = 0; i < unmountedIDs.length; i++) { - var id = unmountedIDs[i]; - purgeDeep(id); - } - unmountedIDs.length = 0; - }, - - isMounted(id: DebugID): boolean { - var item = getItem(id); - return item ? item.isMounted : false; - }, - - getCurrentStackAddendum(topElement: ?ReactElement): string { - var info = ''; - if (topElement) { - var name = getDisplayNameFromElement(topElement); - var owner = topElement._owner; - info += describeComponentFrame( - name, - topElement._source, - owner && getComponentName(owner) - ); - } - - var currentOwner = ReactCurrentOwner.current; - if (currentOwner) { - if (typeof currentOwner.tag === 'number') { - const workInProgress = ((currentOwner : any) : Fiber); - // Safe because if current owner exists, we are reconciling, - // and it is guaranteed to be the work-in-progress version. - info += ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress); - } else if (typeof currentOwner._debugID === 'number') { - info += getStackAddendumByID(currentOwner._debugID); - } - } - return info; - }, - - getStackAddendumByID, - - getChildIDs(id: DebugID): Array { - var item = getItem(id); - return item ? item.childIDs : []; - }, - - getDisplayName(id: DebugID): ?string { - var element = getElement(id); - if (!element) { - return null; - } - return getDisplayNameFromElement(element); - }, - - getElement, - - getOwnerID, - - getParentID, - - getSource(id: DebugID): ?Source { - var item = getItem(id); - var element = item ? item.element : null; - var source = element != null ? element._source : null; - return source; - }, - - getText(id: DebugID): ?string { - var element = getElement(id); - if (typeof element === 'string') { - return element; - } else if (typeof element === 'number') { - return '' + element; - } else { - return null; - } - }, - - getUpdateCount(id: DebugID): number { - var item = getItem(id); - return item ? item.updateCount : 0; - }, - - getRootIDs, - getRegisteredIDs: getItemIDs, - }); -} + getRootIDs, + getRegisteredIDs: getItemIDs, +}; module.exports = ReactComponentTreeHook; diff --git a/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js b/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js index 8dc38050b1..31933d9d86 100644 --- a/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js +++ b/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js @@ -12,7 +12,6 @@ 'use strict'; var DOMProperty = require('DOMProperty'); -var ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook'); var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber'); var warning = require('fbjs/lib/warning'); @@ -20,10 +19,16 @@ var warning = require('fbjs/lib/warning'); var warnedProperties = {}; var rARIA = new RegExp('^(aria)-[' + DOMProperty.ATTRIBUTE_NAME_CHAR + ']*$'); +if (__DEV__) { + var { + getStackAddendumByID, + } = require('react/lib/ReactComponentTreeHook'); +} + function getStackAddendum(debugID) { if (debugID != null) { // This can only happen on Stack - return ReactComponentTreeHook.getStackAddendumByID(debugID); + return getStackAddendumByID && getStackAddendumByID(debugID); } else { // This can only happen on Fiber return ReactDebugCurrentFiber.getCurrentFiberStackAddendum(); diff --git a/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js b/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js index 24b6b64f7f..a8f9a01917 100644 --- a/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js +++ b/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js @@ -11,17 +11,21 @@ 'use strict'; -var ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook'); var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber'); - var warning = require('fbjs/lib/warning'); +if (__DEV__) { + var { + getStackAddendumByID, + } = require('react/lib/ReactComponentTreeHook'); +} + var didWarnValueNull = false; function getStackAddendum(debugID) { if (debugID != null) { // This can only happen on Stack - return ReactComponentTreeHook.getStackAddendumByID(debugID); + return getStackAddendumByID && getStackAddendumByID(debugID); } else { // This can only happen on Fiber return ReactDebugCurrentFiber.getCurrentFiberStackAddendum(); diff --git a/src/renderers/shared/ReactDebugTool.js b/src/renderers/shared/ReactDebugTool.js index b8a3ae7e6e..6a3781f947 100644 --- a/src/renderers/shared/ReactDebugTool.js +++ b/src/renderers/shared/ReactDebugTool.js @@ -14,6 +14,7 @@ var ReactInvalidSetStateWarningHook = require('ReactInvalidSetStateWarningHook'); var ReactHostOperationHistoryHook = require('ReactHostOperationHistoryHook'); +var ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook'); var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment'); var performanceNow = require('fbjs/lib/performanceNow'); @@ -22,7 +23,6 @@ var warning = require('fbjs/lib/warning'); import type { ReactElement } from 'ReactElementType'; import type { DebugID } from 'ReactInstanceType'; import type { Operation } from 'ReactHostOperationHistoryHook'; -import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook'; type Hook = any; @@ -67,20 +67,8 @@ export type FlushHistory = Array; var ReactDebugTool = ((null: any): typeof ReactDebugTool); if (__DEV__) { - const hooks = []; - const didHookThrowForEvent = {}; - const ReactComponentTreeHook: ComponentTreeHookDevType = (require('react/lib/ReactComponentTreeHook'): any); - const { - purgeUnmountedComponents, - getOwnerID, - getParentID, - getDisplayName, - getText, - getUpdateCount, - getChildIDs, - getRegisteredIDs, - getElement, - } = ReactComponentTreeHook; + var hooks = []; + var didHookThrowForEvent = {}; const callHook = function(event, fn, context, arg1, arg2, arg3, arg4, arg5) { try { @@ -120,24 +108,22 @@ if (__DEV__) { var lifeCycleTimerHasWarned = false; const clearHistory = function() { - if (purgeUnmountedComponents) { - purgeUnmountedComponents(); - } + ReactComponentTreeHook.purgeUnmountedComponents(); ReactHostOperationHistoryHook.clearHistory(); }; const getTreeSnapshot = function(registeredIDs) { return registeredIDs.reduce((tree, id) => { - var ownerID = getOwnerID(id); - var parentID = getParentID(id); + var ownerID = ReactComponentTreeHook.getOwnerID(id); + var parentID = ReactComponentTreeHook.getParentID(id); tree[id] = { - displayName: getDisplayName(id), - text: getText(id), - updateCount: getUpdateCount(id), - childIDs: getChildIDs(id), + displayName: ReactComponentTreeHook.getDisplayName(id), + text: ReactComponentTreeHook.getText(id), + updateCount: ReactComponentTreeHook.getUpdateCount(id), + childIDs: ReactComponentTreeHook.getChildIDs(id), // Text nodes don't have owners but this is close enough. ownerID: ownerID || - parentID && getOwnerID(parentID) || + parentID && ReactComponentTreeHook.getOwnerID(parentID) || 0, parentID, }; @@ -158,16 +144,13 @@ if (__DEV__) { } if (previousMeasurements.length || previousOperations.length) { - if (getRegisteredIDs) { - var registeredIDs = getRegisteredIDs(); - - flushHistory.push({ - duration: performanceNow() - previousStartTime, - measurements: previousMeasurements || [], - operations: previousOperations || [], - treeSnapshot: getTreeSnapshot(registeredIDs), - }); - } + var registeredIDs = ReactComponentTreeHook.getRegisteredIDs(); + flushHistory.push({ + duration: performanceNow() - previousStartTime, + measurements: previousMeasurements || [], + operations: previousOperations || [], + treeSnapshot: getTreeSnapshot(registeredIDs), + }); } clearHistory(); @@ -270,7 +253,7 @@ if (__DEV__) { if (!isProfiling || !canUsePerformanceMeasure) { return false; } - var element = getElement && getElement(debugID); + var element = ReactComponentTreeHook.getElement(debugID); if (element == null || typeof element !== 'object') { return false; } @@ -297,7 +280,7 @@ if (__DEV__) { } var markName = `${debugID}::${markType}`; - var displayName = getDisplayName && getDisplayName(debugID) || 'Unknown'; + var displayName = ReactComponentTreeHook.getDisplayName(debugID) || 'Unknown'; // Chrome has an issue of dropping markers recorded too fast: // https://bugs.chromium.org/p/chromium/issues/detail?id=640652 diff --git a/src/renderers/shared/fiber/ReactDebugCurrentFiber.js b/src/renderers/shared/fiber/ReactDebugCurrentFiber.js index 33ad128f72..539c19e085 100644 --- a/src/renderers/shared/fiber/ReactDebugCurrentFiber.js +++ b/src/renderers/shared/fiber/ReactDebugCurrentFiber.js @@ -18,7 +18,7 @@ type LifeCyclePhase = 'render' | 'getChildContext'; if (__DEV__) { var getComponentName = require('getComponentName'); - var { getStackAddendumByWorkInProgressFiber } = require('react/lib/ReactComponentTreeHook'); + var { getStackAddendumByWorkInProgressFiber } = require('ReactFiberComponentTreeHook'); } function getCurrentFiberOwnerName() : string | null { diff --git a/src/renderers/shared/fiber/ReactFiberScheduler.js b/src/renderers/shared/fiber/ReactFiberScheduler.js index cacaacdf83..256f882045 100644 --- a/src/renderers/shared/fiber/ReactFiberScheduler.js +++ b/src/renderers/shared/fiber/ReactFiberScheduler.js @@ -37,7 +37,7 @@ var { const { reset } = require('ReactFiberStack'); var { getStackAddendumByWorkInProgressFiber, -} = require('react/lib/ReactComponentTreeHook'); +} = require('ReactFiberComponentTreeHook'); var { logCapturedError } = require('ReactFiberErrorLogger'); var { invokeGuardedCallback } = require('ReactErrorUtils'); diff --git a/src/renderers/shared/stack/reconciler/ReactRef.js b/src/renderers/shared/stack/reconciler/ReactRef.js index 6705ec9aa3..120c6dddbd 100644 --- a/src/renderers/shared/stack/reconciler/ReactRef.js +++ b/src/renderers/shared/stack/reconciler/ReactRef.js @@ -16,15 +16,12 @@ var ReactOwner = require('ReactOwner'); import type { ReactInstance } from 'ReactInstanceType'; import type { ReactElement } from 'ReactElementType'; -import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook'; var ReactRef = {}; if (__DEV__) { var ReactCompositeComponentTypes = require('ReactCompositeComponentTypes'); - var { - getStackAddendumByID, - }: ComponentTreeHookDevType = (require('react/lib/ReactComponentTreeHook'): any); + var ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook'); var warning = require('fbjs/lib/warning'); var warnedAboutStatelessRefs = {}; @@ -49,14 +46,14 @@ function attachRef(ref, component, owner) { if (element && element._source) { warningKey = element._source.fileName + ':' + element._source.lineNumber; } - if (!warnedAboutStatelessRefs[warningKey] && getStackAddendumByID) { + if (!warnedAboutStatelessRefs[warningKey]) { warnedAboutStatelessRefs[warningKey] = true; warning( false, 'Stateless function components cannot be given refs. ' + 'Attempts to access this ref will fail.%s%s', info, - getStackAddendumByID(component._debugID) + ReactComponentTreeHook.getStackAddendumByID(component._debugID) ); } } diff --git a/src/shared/ReactFiberComponentTreeHook.js b/src/shared/ReactFiberComponentTreeHook.js new file mode 100644 index 0000000000..51cffdb37f --- /dev/null +++ b/src/shared/ReactFiberComponentTreeHook.js @@ -0,0 +1,77 @@ +/** + * Copyright 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + * @providesModule ReactFiberComponentTreeHook + */ + +'use strict'; + +var ReactTypeOfWork = require('ReactTypeOfWork'); +var { + IndeterminateComponent, + FunctionalComponent, + ClassComponent, + HostComponent, +} = ReactTypeOfWork; +var getComponentName = require('getComponentName'); + +import type { Fiber } from 'ReactFiber'; +import type { Source } from 'ReactElementType'; + +function describeComponentFrame(name, source: Source, ownerName) { + return '\n in ' + (name || 'Unknown') + ( + source ? + ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + + source.lineNumber + ')' : + ownerName ? + ' (created by ' + ownerName + ')' : + '' + ); +} + +function describeFiber(fiber : Fiber) : string { + switch (fiber.tag) { + case IndeterminateComponent: + case FunctionalComponent: + case ClassComponent: + case HostComponent: + var owner = fiber._debugOwner; + var source = fiber._debugSource; + var name = getComponentName(fiber); + var ownerName = null; + if (owner) { + ownerName = getComponentName(owner); + } + if (source) { + return describeComponentFrame(name, source, ownerName); + } + return ''; + default: + return ''; + } +} + +// This function can only be called with a work-in-progress fiber and +// only during begin or complete phase. Do not call it under any other +// circumstances. +function getStackAddendumByWorkInProgressFiber(workInProgress : Fiber) : string { + var info = ''; + var node = workInProgress; + do { + info += describeFiber(node); + // Otherwise this return pointer might point to the wrong tree: + node = node.return; + } while (node); + return info; +} + +module.exports = { + getStackAddendumByWorkInProgressFiber, + describeComponentFrame, +}; diff --git a/src/shared/utils/flattenChildren.js b/src/shared/utils/flattenChildren.js index a5a3d85bb5..d07630580f 100644 --- a/src/shared/utils/flattenChildren.js +++ b/src/shared/utils/flattenChildren.js @@ -12,8 +12,6 @@ 'use strict'; -import type { ComponentTreeHookDevType } from 'ReactComponentTreeHook'; - var KeyEscapeUtils = require('KeyEscapeUtils'); var traverseAllChildren = require('traverseAllChildren'); var warning = require('fbjs/lib/warning'); @@ -53,16 +51,14 @@ function flattenSingleChildIntoContext( if (!ReactComponentTreeHook) { ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook'); } - const { getStackAddendumByID }: ComponentTreeHookDevType = (ReactComponentTreeHook: any); - - if (!keyUnique && getStackAddendumByID) { + if (!keyUnique) { warning( false, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), - getStackAddendumByID(selfDebugID) + ReactComponentTreeHook.getStackAddendumByID(selfDebugID) ); } } diff --git a/src/umd/ReactUMDEntry.js b/src/umd/ReactUMDEntry.js index 8cacdceb35..ab926354a6 100644 --- a/src/umd/ReactUMDEntry.js +++ b/src/umd/ReactUMDEntry.js @@ -17,8 +17,17 @@ var React = require('React'); var ReactUMDEntry = Object.assign({ __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { ReactCurrentOwner: require('react/lib/ReactCurrentOwner'), - ReactComponentTreeHook: require('react/lib/ReactComponentTreeHook'), }, }, React); +if (__DEV__) { + Object.assign( + ReactUMDEntry.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + { + // ReactComponentTreeHook should not be included in production. + ReactComponentTreeHook: require('react/lib/ReactComponentTreeHook'), + } + ); +} + module.exports = ReactUMDEntry; From af4d249aa907139e41801b081e14da351ef25485 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 6 Mar 2017 15:15:18 +0000 Subject: [PATCH 10/12] fixes tests so they correctly pass in fiber --- scripts/fiber/tests-passing-except-dev.txt | 58 ------------------- .../hooks/ReactComponentTreeHook.js | 5 +- .../__tests__/ReactComponentTreeHook-test.js | 2 +- src/shared/ReactFiberComponentTreeHook.js | 8 +-- 4 files changed, 4 insertions(+), 69 deletions(-) diff --git a/scripts/fiber/tests-passing-except-dev.txt b/scripts/fiber/tests-passing-except-dev.txt index bd22db6cba..95cad08849 100644 --- a/scripts/fiber/tests-passing-except-dev.txt +++ b/scripts/fiber/tests-passing-except-dev.txt @@ -1,61 +1,3 @@ -src/renderers/__tests__/ReactComponentTreeHook-test.js -* uses displayName or Unknown for classic components -* uses displayName, name, or ReactComponent for modern components -* uses displayName, name, or Object for factory components -* uses displayName, name, or StatelessComponent for functional components -* reports a host tree correctly -* reports a simple tree with composites correctly -* reports a tree with composites correctly -* ignores null children -* ignores false children -* reports text nodes as children -* reports a single text node as a child -* reports a single number node as a child -* reports a zero as a child -* skips empty nodes for multiple children -* reports html content as no children -* updates text of a single text child -* updates from no children to a single text child -* updates from a single text child to no children -* updates from html content to a single text child -* updates from a single text child to html content -* updates from no children to multiple text children -* updates from multiple text children to no children -* updates from html content to multiple text children -* updates from multiple text children to html content -* updates from html content to no children -* updates from no children to html content -* updates from one text child to multiple text children -* updates from multiple text children to one text child -* updates text nodes when reordering -* updates host nodes when reordering with keys -* updates host nodes when reordering without keys -* updates a single composite child of a different type -* updates a single composite child of the same type -* updates from no children to a single composite child -* updates from a single composite child to no children -* updates mixed children -* updates with a host child -* updates from null to a host child -* updates from a host child to null -* updates from a host child to a composite child -* updates from a composite child to a host child -* updates from null to a composite child -* updates from a composite child to null -* updates with a host child -* updates from null to a host child -* updates from a host child to null -* updates from a host child to a composite child -* updates from a composite child to a host child -* updates from null to a composite child -* updates from a composite child to null -* tracks owner correctly -* purges unmounted components automatically -* reports update counts -* does not report top-level wrapper as a root -* registers inlined text nodes -* works - src/renderers/__tests__/ReactComponentTreeHook-test.native.js * uses displayName or Unknown for classic components * uses displayName, name, or ReactComponent for modern components diff --git a/src/isomorphic/hooks/ReactComponentTreeHook.js b/src/isomorphic/hooks/ReactComponentTreeHook.js index 9fede1ac22..332c5cac8c 100644 --- a/src/isomorphic/hooks/ReactComponentTreeHook.js +++ b/src/isomorphic/hooks/ReactComponentTreeHook.js @@ -182,10 +182,7 @@ function describeID(id: DebugID): string { 'building stack', id ); - if (element && name) { - return describeComponentFrame(name || '', element._source, ownerName || ''); - } - return ''; + return describeComponentFrame(name || '', element && element._source, ownerName || ''); } var ReactComponentTreeHook = { diff --git a/src/renderers/__tests__/ReactComponentTreeHook-test.js b/src/renderers/__tests__/ReactComponentTreeHook-test.js index a9c19c13eb..0e92d9bfdf 100644 --- a/src/renderers/__tests__/ReactComponentTreeHook-test.js +++ b/src/renderers/__tests__/ReactComponentTreeHook-test.js @@ -1736,7 +1736,7 @@ describe('ReactComponentTreeHook', () => { expectDev(ReactComponentTreeTestUtils.getRegisteredDisplayNames()).toEqual([]); }); - describe('stack addenda', () => { + describe.only('stack addenda', () => { it('gets created', () => { function getAddendum(element) { var addendum = ReactComponentTreeHook.getCurrentStackAddendum(element); diff --git a/src/shared/ReactFiberComponentTreeHook.js b/src/shared/ReactFiberComponentTreeHook.js index 51cffdb37f..df24a78b0d 100644 --- a/src/shared/ReactFiberComponentTreeHook.js +++ b/src/shared/ReactFiberComponentTreeHook.js @@ -22,9 +22,8 @@ var { var getComponentName = require('getComponentName'); import type { Fiber } from 'ReactFiber'; -import type { Source } from 'ReactElementType'; -function describeComponentFrame(name, source: Source, ownerName) { +function describeComponentFrame(name, source: any, ownerName) { return '\n in ' + (name || 'Unknown') + ( source ? ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + @@ -48,10 +47,7 @@ function describeFiber(fiber : Fiber) : string { if (owner) { ownerName = getComponentName(owner); } - if (source) { - return describeComponentFrame(name, source, ownerName); - } - return ''; + return describeComponentFrame(name, source, ownerName); default: return ''; } From f992d5415752da7413b2821783407762fd9e29f5 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 6 Mar 2017 15:18:21 +0000 Subject: [PATCH 11/12] removed describe.only --- scripts/fiber/tests-passing-except-dev.txt | 58 +++++++++++++++++++ .../__tests__/ReactComponentTreeHook-test.js | 2 +- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/scripts/fiber/tests-passing-except-dev.txt b/scripts/fiber/tests-passing-except-dev.txt index 95cad08849..bd22db6cba 100644 --- a/scripts/fiber/tests-passing-except-dev.txt +++ b/scripts/fiber/tests-passing-except-dev.txt @@ -1,3 +1,61 @@ +src/renderers/__tests__/ReactComponentTreeHook-test.js +* uses displayName or Unknown for classic components +* uses displayName, name, or ReactComponent for modern components +* uses displayName, name, or Object for factory components +* uses displayName, name, or StatelessComponent for functional components +* reports a host tree correctly +* reports a simple tree with composites correctly +* reports a tree with composites correctly +* ignores null children +* ignores false children +* reports text nodes as children +* reports a single text node as a child +* reports a single number node as a child +* reports a zero as a child +* skips empty nodes for multiple children +* reports html content as no children +* updates text of a single text child +* updates from no children to a single text child +* updates from a single text child to no children +* updates from html content to a single text child +* updates from a single text child to html content +* updates from no children to multiple text children +* updates from multiple text children to no children +* updates from html content to multiple text children +* updates from multiple text children to html content +* updates from html content to no children +* updates from no children to html content +* updates from one text child to multiple text children +* updates from multiple text children to one text child +* updates text nodes when reordering +* updates host nodes when reordering with keys +* updates host nodes when reordering without keys +* updates a single composite child of a different type +* updates a single composite child of the same type +* updates from no children to a single composite child +* updates from a single composite child to no children +* updates mixed children +* updates with a host child +* updates from null to a host child +* updates from a host child to null +* updates from a host child to a composite child +* updates from a composite child to a host child +* updates from null to a composite child +* updates from a composite child to null +* updates with a host child +* updates from null to a host child +* updates from a host child to null +* updates from a host child to a composite child +* updates from a composite child to a host child +* updates from null to a composite child +* updates from a composite child to null +* tracks owner correctly +* purges unmounted components automatically +* reports update counts +* does not report top-level wrapper as a root +* registers inlined text nodes +* works + src/renderers/__tests__/ReactComponentTreeHook-test.native.js * uses displayName or Unknown for classic components * uses displayName, name, or ReactComponent for modern components diff --git a/src/renderers/__tests__/ReactComponentTreeHook-test.js b/src/renderers/__tests__/ReactComponentTreeHook-test.js index 0e92d9bfdf..a9c19c13eb 100644 --- a/src/renderers/__tests__/ReactComponentTreeHook-test.js +++ b/src/renderers/__tests__/ReactComponentTreeHook-test.js @@ -1736,7 +1736,7 @@ describe('ReactComponentTreeHook', () => { expectDev(ReactComponentTreeTestUtils.getRegisteredDisplayNames()).toEqual([]); }); - describe.only('stack addenda', () => { + describe('stack addenda', () => { it('gets created', () => { function getAddendum(element) { var addendum = ReactComponentTreeHook.getCurrentStackAddendum(element); From a159e85c469cb2ad336d6976eaf11cf12ccbde10 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 7 Mar 2017 13:35:41 +0000 Subject: [PATCH 12/12] removed inline boolean checks for methods --- src/isomorphic/classic/element/ReactElementValidator.js | 4 ++-- src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js | 2 +- .../dom/shared/hooks/ReactDOMNullInputValuePropHook.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/isomorphic/classic/element/ReactElementValidator.js b/src/isomorphic/classic/element/ReactElementValidator.js index 7f30253349..f2a699dc9e 100644 --- a/src/isomorphic/classic/element/ReactElementValidator.js +++ b/src/isomorphic/classic/element/ReactElementValidator.js @@ -124,7 +124,7 @@ function validateExplicitKey(element, parentType) { '%s%s See https://fb.me/react-warning-keys for more information.%s', currentComponentErrorInfo, childOwner, - getCurrentStackAddendum && getCurrentStackAddendum(element) + getCurrentStackAddendum(element) ); } @@ -227,7 +227,7 @@ var ReactElementValidator = { info += getDeclarationErrorAddendum(); } - info += getCurrentStackAddendum && getCurrentStackAddendum(); + info += getCurrentStackAddendum(); warning( false, diff --git a/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js b/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js index 31933d9d86..098023cde7 100644 --- a/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js +++ b/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js @@ -28,7 +28,7 @@ if (__DEV__) { function getStackAddendum(debugID) { if (debugID != null) { // This can only happen on Stack - return getStackAddendumByID && getStackAddendumByID(debugID); + return getStackAddendumByID(debugID); } else { // This can only happen on Fiber return ReactDebugCurrentFiber.getCurrentFiberStackAddendum(); diff --git a/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js b/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js index a8f9a01917..a1e8cfbe16 100644 --- a/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js +++ b/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js @@ -25,7 +25,7 @@ var didWarnValueNull = false; function getStackAddendum(debugID) { if (debugID != null) { // This can only happen on Stack - return getStackAddendumByID && getStackAddendumByID(debugID); + return getStackAddendumByID(debugID); } else { // This can only happen on Fiber return ReactDebugCurrentFiber.getCurrentFiberStackAddendum();