[Fiber] Support React DevTools (#8818)
* Initial React DevTools support for Fiber * Record a newly failing irrelevant test This is fine because we don't use it in Stack anyway (at least not that part), and we also rely on Set/Map in other parts of code. The newly failing test is the one saying "it works without Map/Set". * Explicitly check for Fiber DevTools support This lets us ignore pre-Fiber DevTools instead of crashing them by injecting a different API. * Track the roots in DevTools instead * Lol Dan this is not how warnings work in our codebase * Move injection to the renderer * Notify DevTools about unmounts * Make it work in production * Fix lint and flow * Fixups * Address feedback
Dan Abramov committed
Jan 27, 2017 at 22:50 UTC
d3e29d4e54f696da915f9aec08202b6314a162a1
4 files changed
+91
-1
src/renderers/dom/fiber/ReactDOMFiber.js
+8
-1
@@ -29,7 +29,7 @@ var ReactInputSelection = require('ReactInputSelection');
29
var ReactInstanceMap = require('ReactInstanceMap');
30
var ReactPortal = require('ReactPortal');
31
var { isValidElement } = require('React');
32
-
32
+var { injectInternals } = require('ReactFiberDevToolsHook');
33
34
var findDOMNode = require('findDOMNode');
35
var invariant = require('invariant');
@@ -407,4 +407,11 @@ var ReactDOM = {
407
408
};
409
410
+if (typeof injectInternals === 'function') {
411
+ injectInternals({
412
+ findFiberByHostInstance: ReactDOMComponentTree.getClosestInstanceFromNode,
413
+ findHostInstanceByFiber: DOMRenderer.findHostInstance,
414
+ });
415
+}
416
+
417
module.exports = ReactDOM;
src/renderers/shared/fiber/ReactFiberCommitWork.js
+5
@@ -25,6 +25,7 @@ var {
25
CoroutineComponent,
26
} = ReactTypeOfWork;
27
var { commitCallbacks } = require('ReactFiberUpdateQueue');
28
+var { onCommitUnmount } = require('ReactFiberDevToolsHook');
29
30
var {
31
Placement,
@@ -317,6 +318,10 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
318
// deletion, so don't let them throw. Host-originating errors should
319
// interrupt deletion, so it's okay
320
function commitUnmount(current : Fiber) : void {
321
+ if (typeof onCommitUnmount === 'function') {
322
+ onCommitUnmount(current);
323
+ }
324
+
325
switch (current.tag) {
326
case ClassComponent: {
327
safelyDetachRef(current);
src/renderers/shared/fiber/ReactFiberDevToolsHook.js
new
+74
@@ -0,0 +1,74 @@
1
+/**
2
+ * Copyright 2013-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule ReactFiberDevToolsHook
10
+ * @flow
11
+ */
12
+
13
+/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__ */
14
+
15
+'use strict';
16
+
17
+import type { Fiber } from 'ReactFiber';
18
+import type { FiberRoot } from 'ReactFiberRoot';
19
+
20
+if (__DEV__) {
21
+ var warning = require('warning');
22
+}
23
+
24
+let rendererID = null;
25
+let injectInternals = null;
26
+let onCommitRoot = null;
27
+let onCommitUnmount = null;
28
+if (
29
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
30
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.supportsFiber
31
+) {
32
+ let {
33
+ inject,
34
+ onCommitFiberRoot,
35
+ onCommitFiberUnmount,
36
+ } = __REACT_DEVTOOLS_GLOBAL_HOOK__;
37
+
38
+ injectInternals = function(internals : Object) {
39
+ warning(rendererID == null, 'Cannot inject into DevTools twice.');
40
+ rendererID = inject(internals);
41
+ };
42
+
43
+ onCommitRoot = function(root : FiberRoot) {
44
+ if (rendererID == null) {
45
+ return;
46
+ }
47
+ try {
48
+ onCommitFiberRoot(rendererID, root);
49
+ } catch (err) {
50
+ // Catch all errors because it is unsafe to throw in the commit phase.
51
+ if (__DEV__) {
52
+ warning(false, 'React DevTools encountered an error: %s', err);
53
+ }
54
+ }
55
+ };
56
+
57
+ onCommitUnmount = function(fiber : Fiber) {
58
+ if (rendererID == null) {
59
+ return;
60
+ }
61
+ try {
62
+ onCommitFiberUnmount(rendererID, fiber);
63
+ } catch (err) {
64
+ // Catch all errors because it is unsafe to throw in the commit phase.
65
+ if (__DEV__) {
66
+ warning(false, 'React DevTools encountered an error: %s', err);
67
+ }
68
+ }
69
+ };
70
+}
71
+
72
+exports.injectInternals = injectInternals;
73
+exports.onCommitRoot = onCommitRoot;
74
+exports.onCommitUnmount = onCommitUnmount;
src/renderers/shared/fiber/ReactFiberScheduler.js
+4
@@ -44,6 +44,7 @@ var ReactFeatureFlags = require('ReactFeatureFlags');
44
var getComponentName = require('getComponentName');
45
46
var { cloneFiber } = require('ReactFiber');
47
+var { onCommitRoot } = require('ReactFiberDevToolsHook');
48
49
var {
50
NoWork,
@@ -411,6 +412,9 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
412
}
413
414
isCommitting = false;
415
+ if (typeof onCommitRoot === 'function') {
416
+ onCommitRoot(finishedWork.stateNode);
417
+ }
418
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
419
ReactFiberInstrumentation.debugTool.onCommitWork(finishedWork);
420
}