From 3863330524d99b5ebedbfa65bc09fa46ae7fa8a6 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 18 Feb 2016 22:52:19 +0000 Subject: [PATCH] Add ReactInstrumentation This adds `ReactInstrumentation` for the isomorphic package that uses the same approach as `ReactDOMInstrumentation`. Currently it is gated behind `__DEV__` but we will likely change this later to a runtime flag determined by whether there are any active listeners. The first few events we add here should be sufficient for React DevTools, as determined by the `hook.emit()` calls in https://github.com/facebook/react-devtools/blob/d90c43201617d5aef378669da926d79515b6a811/backend/attachRenderer.js. These events will also be useful for reconstructing the parent tree in the ReactPerf rewrite in #6046. --- src/isomorphic/ReactDebugTool.js | 65 +++++++++++++++++++ src/isomorphic/ReactInstrumentation.js | 16 +++++ src/renderers/dom/client/ReactMount.js | 5 ++ .../shared/reconciler/ReactReconciler.js | 16 ++++- 4 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/isomorphic/ReactDebugTool.js create mode 100644 src/isomorphic/ReactInstrumentation.js diff --git a/src/isomorphic/ReactDebugTool.js b/src/isomorphic/ReactDebugTool.js new file mode 100644 index 000000000000..2435a613f400 --- /dev/null +++ b/src/isomorphic/ReactDebugTool.js @@ -0,0 +1,65 @@ +/** + * 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. + * + * @providesModule ReactDebugTool + */ + +'use strict'; + +var warning = require('warning'); + +var eventHandlers = []; +var handlerDoesThrowForEvent = {}; + +function emitEvent(handlerFunctionName, arg1, arg2, arg3, arg4, arg5) { + if (__DEV__) { + eventHandlers.forEach(function(handler) { + try { + if (handler[handlerFunctionName]) { + handler[handlerFunctionName](arg1, arg2, arg3, arg4, arg5); + } + } catch (e) { + warning( + !handlerDoesThrowForEvent[handlerFunctionName], + 'exception thrown by devtool while handling %s: %s', + handlerFunctionName, + e.message + ); + handlerDoesThrowForEvent[handlerFunctionName] = true; + } + }); + } +} + +var ReactDebugTool = { + addDevtool(devtool) { + eventHandlers.push(devtool); + }, + removeDevtool(devtool) { + for (var i = 0; i < eventHandlers.length; i++) { + if (eventHandlers[i] === devtool) { + eventHandlers.splice(i, 1); + i--; + } + } + }, + onMountRootComponent(internalInstance) { + emitEvent('onMountRootComponent', internalInstance); + }, + onMountComponent(internalInstance) { + emitEvent('onMountComponent', internalInstance); + }, + onUpdateComponent(internalInstance) { + emitEvent('onUpdateComponent', internalInstance); + }, + onUnmountComponent(internalInstance) { + emitEvent('onUnmountComponent', internalInstance); + }, +}; + +module.exports = ReactDebugTool; diff --git a/src/isomorphic/ReactInstrumentation.js b/src/isomorphic/ReactInstrumentation.js new file mode 100644 index 000000000000..066da6aaae82 --- /dev/null +++ b/src/isomorphic/ReactInstrumentation.js @@ -0,0 +1,16 @@ +/** + * 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. + * + * @providesModule ReactInstrumentation + */ + +'use strict'; + +var ReactDebugTool = require('ReactDebugTool'); + +module.exports = {debugTool: ReactDebugTool}; diff --git a/src/renderers/dom/client/ReactMount.js b/src/renderers/dom/client/ReactMount.js index ea62047b0aba..4b11559670cd 100644 --- a/src/renderers/dom/client/ReactMount.js +++ b/src/renderers/dom/client/ReactMount.js @@ -20,6 +20,7 @@ var ReactDOMContainerInfo = require('ReactDOMContainerInfo'); var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); var ReactElement = require('ReactElement'); var ReactFeatureFlags = require('ReactFeatureFlags'); +var ReactInstrumentation = require('ReactInstrumentation'); var ReactMarkupChecksum = require('ReactMarkupChecksum'); var ReactPerf = require('ReactPerf'); var ReactReconciler = require('ReactReconciler'); @@ -347,6 +348,10 @@ var ReactMount = { var wrapperID = componentInstance._instance.rootID; instancesByReactRootID[wrapperID] = componentInstance; + if (__DEV__) { + ReactInstrumentation.debugTool.onMountRootComponent(componentInstance); + } + return componentInstance; }, diff --git a/src/renderers/shared/reconciler/ReactReconciler.js b/src/renderers/shared/reconciler/ReactReconciler.js index 507d351612a9..ae7806e4fc0e 100644 --- a/src/renderers/shared/reconciler/ReactReconciler.js +++ b/src/renderers/shared/reconciler/ReactReconciler.js @@ -12,6 +12,7 @@ 'use strict'; var ReactRef = require('ReactRef'); +var ReactInstrumentation = require('ReactInstrumentation'); /** * Helper to call ReactRef.attachRefs with this composite component, split out @@ -51,6 +52,9 @@ var ReactReconciler = { internalInstance._currentElement.ref != null) { transaction.getReactMountReady().enqueue(attachRefs, internalInstance); } + if (__DEV__) { + ReactInstrumentation.debugTool.onMountComponent(internalInstance); + } return markup; }, @@ -70,7 +74,10 @@ var ReactReconciler = { */ unmountComponent: function(internalInstance, safely) { ReactRef.detachRefs(internalInstance, internalInstance._currentElement); - return internalInstance.unmountComponent(safely); + internalInstance.unmountComponent(safely); + if (__DEV__) { + ReactInstrumentation.debugTool.onUnmountComponent(internalInstance); + } }, /** @@ -119,6 +126,10 @@ var ReactReconciler = { internalInstance._currentElement.ref != null) { transaction.getReactMountReady().enqueue(attachRefs, internalInstance); } + + if (__DEV__) { + ReactInstrumentation.debugTool.onUpdateComponent(internalInstance); + } }, /** @@ -133,6 +144,9 @@ var ReactReconciler = { transaction ) { internalInstance.performUpdateIfNecessary(transaction); + if (__DEV__) { + ReactInstrumentation.debugTool.onUpdateComponent(internalInstance); + } }, };