From b572e3c907e31700b140d750fd77977f0fd15885 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 6 Mar 2017 14:19:23 +0000 Subject: [PATCH] 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;