mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Type ReactComponentTreeHook (#7504)
For this one, I wanted to type a non-trivial piece of the codebase and ran into the fact that we do not have types for ReactElement nor ReactInstance, so I had to create them. I'll add comments inline
This commit is contained in:
committed by
GitHub
parent
563f3bbab4
commit
ea494a2c10
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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 ReactElementType
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type { ReactInstance } from 'ReactInstanceType';
|
||||
|
||||
export type Source = {
|
||||
fileName: string,
|
||||
lineNumber: number,
|
||||
};
|
||||
|
||||
export type ReactElement = {
|
||||
$$typeof: any,
|
||||
type: any,
|
||||
key: any,
|
||||
ref: any,
|
||||
props: any,
|
||||
_owner: ReactInstance,
|
||||
|
||||
// __DEV__
|
||||
_store: {
|
||||
validated: bool,
|
||||
},
|
||||
_self: ReactElement,
|
||||
_shadowChildren: any,
|
||||
_source: Source,
|
||||
};
|
||||
@@ -6,6 +6,7 @@
|
||||
* 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 ReactComponentTreeHook
|
||||
*/
|
||||
|
||||
@@ -16,6 +17,9 @@ var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
var invariant = require('invariant');
|
||||
var warning = require('warning');
|
||||
|
||||
import type { ReactElement, Source } from 'ReactElementType';
|
||||
import type { DebugID } from 'ReactInstanceType';
|
||||
|
||||
function isNative(fn) {
|
||||
// Based on isNative() from Lodash
|
||||
var funcToString = Function.prototype.toString;
|
||||
@@ -39,6 +43,24 @@ function isNative(fn) {
|
||||
}
|
||||
}
|
||||
|
||||
type Item = {
|
||||
element: ReactElement,
|
||||
parentID: DebugID,
|
||||
text: ?string,
|
||||
childIDs: Array<DebugID>,
|
||||
isMounted: bool,
|
||||
updateCount: number,
|
||||
};
|
||||
|
||||
declare function getItem(id: DebugID): ?Item;
|
||||
declare function removeItem(id: DebugID): void;
|
||||
declare function setItem(id: DebugID, item: Item): void;
|
||||
declare function getItemIDs(): Array<DebugID>;
|
||||
|
||||
declare function addRoot(id: DebugID): void;
|
||||
declare function removeRoot(id: DebugID): void;
|
||||
declare function getRootIDs(): Array<DebugID>;
|
||||
|
||||
var canUseCollections = (
|
||||
// Array.from
|
||||
typeof Array.from === 'function' &&
|
||||
@@ -58,112 +80,88 @@ var canUseCollections = (
|
||||
isNative(Set.prototype.keys)
|
||||
);
|
||||
|
||||
var itemMap;
|
||||
var rootIDSet;
|
||||
|
||||
var itemByKey;
|
||||
var rootByKey;
|
||||
|
||||
if (canUseCollections) {
|
||||
itemMap = new Map();
|
||||
rootIDSet = new Set();
|
||||
} else {
|
||||
itemByKey = {};
|
||||
rootByKey = {};
|
||||
}
|
||||
var itemMap = new Map();
|
||||
var rootIDSet = new Set();
|
||||
|
||||
var unmountedIDs = [];
|
||||
|
||||
// Use non-numeric keys to prevent V8 performance issues:
|
||||
// https://github.com/facebook/react/pull/7232
|
||||
function getKeyFromID(id) {
|
||||
return '.' + id;
|
||||
}
|
||||
function getIDFromKey(key) {
|
||||
return parseInt(key.substr(1), 10);
|
||||
}
|
||||
|
||||
function get(id) {
|
||||
if (canUseCollections) {
|
||||
var setItem = function(id, item) {
|
||||
itemMap.set(id, item);
|
||||
};
|
||||
var getItem = function(id) {
|
||||
return itemMap.get(id);
|
||||
} else {
|
||||
var key = getKeyFromID(id);
|
||||
return itemByKey[key];
|
||||
}
|
||||
}
|
||||
|
||||
function remove(id) {
|
||||
if (canUseCollections) {
|
||||
};
|
||||
var removeItem = function(id) {
|
||||
itemMap.delete(id);
|
||||
} else {
|
||||
var key = getKeyFromID(id);
|
||||
delete itemByKey[key];
|
||||
}
|
||||
}
|
||||
|
||||
function create(id, element, parentID) {
|
||||
var item = {
|
||||
element,
|
||||
parentID,
|
||||
text: null,
|
||||
childIDs: [],
|
||||
isMounted: false,
|
||||
updateCount: 0,
|
||||
};
|
||||
var getItemIDs = function() {
|
||||
return Array.from(itemMap.keys());
|
||||
};
|
||||
|
||||
if (canUseCollections) {
|
||||
itemMap.set(id, item);
|
||||
} else {
|
||||
var addRoot = function(id) {
|
||||
rootIDSet.add(id);
|
||||
};
|
||||
var removeRoot = function(id) {
|
||||
rootIDSet.delete(id);
|
||||
};
|
||||
var 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
|
||||
function getKeyFromID(id: DebugID): string {
|
||||
return '.' + id;
|
||||
}
|
||||
function getIDFromKey(key: string): DebugID {
|
||||
return parseInt(key.substr(1), 10);
|
||||
}
|
||||
|
||||
var setItem = function(id, item) {
|
||||
var key = getKeyFromID(id);
|
||||
itemByKey[key] = item;
|
||||
}
|
||||
}
|
||||
};
|
||||
var getItem = function(id) {
|
||||
var key = getKeyFromID(id);
|
||||
return itemByKey[key];
|
||||
};
|
||||
var removeItem = function(id) {
|
||||
var key = getKeyFromID(id);
|
||||
delete itemByKey[key];
|
||||
};
|
||||
var getItemIDs = function() {
|
||||
return Object.keys(itemByKey).map(getIDFromKey);
|
||||
};
|
||||
|
||||
function addRoot(id) {
|
||||
if (canUseCollections) {
|
||||
rootIDSet.add(id);
|
||||
} else {
|
||||
var addRoot = function(id) {
|
||||
var key = getKeyFromID(id);
|
||||
rootByKey[key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
function removeRoot(id) {
|
||||
if (canUseCollections) {
|
||||
rootIDSet.delete(id);
|
||||
} else {
|
||||
};
|
||||
var removeRoot = function(id) {
|
||||
var key = getKeyFromID(id);
|
||||
delete rootByKey[key];
|
||||
}
|
||||
}
|
||||
|
||||
function getRegisteredIDs() {
|
||||
if (canUseCollections) {
|
||||
return Array.from(itemMap.keys());
|
||||
} else {
|
||||
return Object.keys(itemByKey).map(getIDFromKey);
|
||||
}
|
||||
}
|
||||
|
||||
function getRootIDs() {
|
||||
if (canUseCollections) {
|
||||
return Array.from(rootIDSet.keys());
|
||||
} else {
|
||||
};
|
||||
var getRootIDs = function() {
|
||||
return Object.keys(rootByKey).map(getIDFromKey);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var unmountedIDs: Array<DebugID> = [];
|
||||
|
||||
function purgeDeep(id) {
|
||||
var item = get(id);
|
||||
var item = getItem(id);
|
||||
if (item) {
|
||||
var {childIDs} = item;
|
||||
remove(id);
|
||||
removeItem(id);
|
||||
childIDs.forEach(purgeDeep);
|
||||
}
|
||||
}
|
||||
|
||||
function describeComponentFrame(name, source, ownerName) {
|
||||
return '\n in ' + name + (
|
||||
return '\n in ' + (name || 'Unknown') + (
|
||||
source ?
|
||||
' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' +
|
||||
source.lineNumber + ')' :
|
||||
@@ -173,7 +171,7 @@ function describeComponentFrame(name, source, ownerName) {
|
||||
);
|
||||
}
|
||||
|
||||
function getDisplayName(element) {
|
||||
function getDisplayName(element: ?ReactElement): string {
|
||||
if (element == null) {
|
||||
return '#empty';
|
||||
} else if (typeof element === 'string' || typeof element === 'number') {
|
||||
@@ -185,7 +183,7 @@ function getDisplayName(element) {
|
||||
}
|
||||
}
|
||||
|
||||
function describeID(id) {
|
||||
function describeID(id: DebugID): string {
|
||||
var name = ReactComponentTreeHook.getDisplayName(id);
|
||||
var element = ReactComponentTreeHook.getElement(id);
|
||||
var ownerID = ReactComponentTreeHook.getOwnerID(id);
|
||||
@@ -203,13 +201,14 @@ function describeID(id) {
|
||||
}
|
||||
|
||||
var ReactComponentTreeHook = {
|
||||
onSetChildren(id, nextChildIDs) {
|
||||
var item = get(id);
|
||||
onSetChildren(id: DebugID, nextChildIDs: Array<DebugID>): 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 = get(nextChildID);
|
||||
var nextChild = getItem(nextChildID);
|
||||
invariant(
|
||||
nextChild,
|
||||
'Expected hook events to fire for the child ' +
|
||||
@@ -231,7 +230,7 @@ var ReactComponentTreeHook = {
|
||||
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.
|
||||
// be purged from our tree data so their parent id is missing.
|
||||
}
|
||||
invariant(
|
||||
nextChild.parentID === id,
|
||||
@@ -244,12 +243,20 @@ var ReactComponentTreeHook = {
|
||||
}
|
||||
},
|
||||
|
||||
onBeforeMountComponent(id, element, parentID) {
|
||||
create(id, element, parentID);
|
||||
onBeforeMountComponent(id: DebugID, element: ReactElement, parentID: DebugID): void {
|
||||
var item = {
|
||||
element,
|
||||
parentID,
|
||||
text: null,
|
||||
childIDs: [],
|
||||
isMounted: false,
|
||||
updateCount: 0,
|
||||
};
|
||||
setItem(id, item);
|
||||
},
|
||||
|
||||
onBeforeUpdateComponent(id, element) {
|
||||
var item = get(id);
|
||||
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.
|
||||
@@ -258,8 +265,9 @@ var ReactComponentTreeHook = {
|
||||
item.element = element;
|
||||
},
|
||||
|
||||
onMountComponent(id) {
|
||||
var item = get(id);
|
||||
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) {
|
||||
@@ -267,8 +275,8 @@ var ReactComponentTreeHook = {
|
||||
}
|
||||
},
|
||||
|
||||
onUpdateComponent(id) {
|
||||
var item = get(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.
|
||||
@@ -277,8 +285,8 @@ var ReactComponentTreeHook = {
|
||||
item.updateCount++;
|
||||
},
|
||||
|
||||
onUnmountComponent(id) {
|
||||
var item = get(id);
|
||||
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
|
||||
@@ -294,7 +302,7 @@ var ReactComponentTreeHook = {
|
||||
unmountedIDs.push(id);
|
||||
},
|
||||
|
||||
purgeUnmountedComponents() {
|
||||
purgeUnmountedComponents(): void {
|
||||
if (ReactComponentTreeHook._preventPurging) {
|
||||
// Should only be used for testing.
|
||||
return;
|
||||
@@ -307,21 +315,18 @@ var ReactComponentTreeHook = {
|
||||
unmountedIDs.length = 0;
|
||||
},
|
||||
|
||||
isMounted(id) {
|
||||
var item = get(id);
|
||||
isMounted(id: DebugID): bool {
|
||||
var item = getItem(id);
|
||||
return item ? item.isMounted : false;
|
||||
},
|
||||
|
||||
getCurrentStackAddendum(topElement) {
|
||||
getCurrentStackAddendum(topElement: ?ReactElement): string {
|
||||
var info = '';
|
||||
if (topElement) {
|
||||
var type = topElement.type;
|
||||
var name = typeof type === 'function' ?
|
||||
type.displayName || type.name :
|
||||
type;
|
||||
var name = getDisplayName(topElement);
|
||||
var owner = topElement._owner;
|
||||
info += describeComponentFrame(
|
||||
name || 'Unknown',
|
||||
name,
|
||||
topElement._source,
|
||||
owner && owner.getName()
|
||||
);
|
||||
@@ -334,7 +339,7 @@ var ReactComponentTreeHook = {
|
||||
return info;
|
||||
},
|
||||
|
||||
getStackAddendumByID(id) {
|
||||
getStackAddendumByID(id: ?DebugID): string {
|
||||
var info = '';
|
||||
while (id) {
|
||||
info += describeID(id);
|
||||
@@ -343,12 +348,12 @@ var ReactComponentTreeHook = {
|
||||
return info;
|
||||
},
|
||||
|
||||
getChildIDs(id) {
|
||||
var item = get(id);
|
||||
getChildIDs(id: DebugID): Array<DebugID> {
|
||||
var item = getItem(id);
|
||||
return item ? item.childIDs : [];
|
||||
},
|
||||
|
||||
getDisplayName(id) {
|
||||
getDisplayName(id: DebugID): ?string {
|
||||
var element = ReactComponentTreeHook.getElement(id);
|
||||
if (!element) {
|
||||
return null;
|
||||
@@ -356,12 +361,12 @@ var ReactComponentTreeHook = {
|
||||
return getDisplayName(element);
|
||||
},
|
||||
|
||||
getElement(id) {
|
||||
var item = get(id);
|
||||
getElement(id: DebugID): ?ReactElement {
|
||||
var item = getItem(id);
|
||||
return item ? item.element : null;
|
||||
},
|
||||
|
||||
getOwnerID(id) {
|
||||
getOwnerID(id: DebugID): ?DebugID {
|
||||
var element = ReactComponentTreeHook.getElement(id);
|
||||
if (!element || !element._owner) {
|
||||
return null;
|
||||
@@ -369,19 +374,19 @@ var ReactComponentTreeHook = {
|
||||
return element._owner._debugID;
|
||||
},
|
||||
|
||||
getParentID(id) {
|
||||
var item = get(id);
|
||||
getParentID(id: DebugID): ?DebugID {
|
||||
var item = getItem(id);
|
||||
return item ? item.parentID : null;
|
||||
},
|
||||
|
||||
getSource(id) {
|
||||
var item = get(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) {
|
||||
getText(id: DebugID): ?string {
|
||||
var element = ReactComponentTreeHook.getElement(id);
|
||||
if (typeof element === 'string') {
|
||||
return element;
|
||||
@@ -392,14 +397,13 @@ var ReactComponentTreeHook = {
|
||||
}
|
||||
},
|
||||
|
||||
getUpdateCount(id) {
|
||||
var item = get(id);
|
||||
getUpdateCount(id: DebugID): number {
|
||||
var item = getItem(id);
|
||||
return item ? item.updateCount : 0;
|
||||
},
|
||||
|
||||
getRegisteredIDs,
|
||||
|
||||
getRootIDs,
|
||||
getRegisteredIDs: getItemIDs,
|
||||
};
|
||||
|
||||
module.exports = ReactComponentTreeHook;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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 ReactInstanceType
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export type DebugID = number;
|
||||
|
||||
export type ReactInstance = {
|
||||
// ReactCompositeComponent
|
||||
mountComponent: any,
|
||||
performInitialMountWithErrorHandling: any,
|
||||
performInitialMount: any,
|
||||
getHostNode: any,
|
||||
unmountComponent: any,
|
||||
receiveComponent: any,
|
||||
performUpdateIfNecessary: any,
|
||||
updateComponent: any,
|
||||
attachRef: any,
|
||||
detachRef: any,
|
||||
getName: () => string,
|
||||
getPublicInstance: any,
|
||||
|
||||
// instantiateReactComponent
|
||||
_mountIndex: number,
|
||||
_mountImage: any,
|
||||
// __DEV__
|
||||
_debugID: DebugID,
|
||||
};
|
||||
Reference in New Issue
Block a user