diff --git a/shells/dev/app/App.css b/shells/dev/app/App.css
deleted file mode 100644
index 890641f080..0000000000
--- a/shells/dev/app/App.css
+++ /dev/null
@@ -1,7 +0,0 @@
-.App {
- /* GitHub.com frontend fonts */
- font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
- sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
- font-size: 14px;
- line-height: 1.5;
-}
diff --git a/shells/dev/app/App.js b/shells/dev/app/App.js
deleted file mode 100644
index 787a5af9fd..0000000000
--- a/shells/dev/app/App.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// @flow
-
-import React from 'react';
-import ElementTypes from './ElementTypes';
-import InspectableElements from './InspectableElements';
-import List from './ToDoList';
-import styles from './App.css';
-
-export default function App() {
- return (
-
-
-
-
-
- );
-}
diff --git a/shells/dev/app/InspectableElements/InspectableElements.css b/shells/dev/app/InspectableElements/InspectableElements.css
index 6d36bfb51d..b1ebb8ee71 100644
--- a/shells/dev/app/InspectableElements/InspectableElements.css
+++ b/shells/dev/app/InspectableElements/InspectableElements.css
@@ -1,3 +1,11 @@
+.App {
+ /* GitHub.com frontend fonts */
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
+ sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
+ font-size: 14px;
+ line-height: 1.5;
+}
+
.Header {
font-size: 1.5rem;
font-weight: bold;
diff --git a/shells/dev/app/InspectableElements/InspectableElements.js b/shells/dev/app/InspectableElements/InspectableElements.js
index fa814cceda..b7cbbf2937 100644
--- a/shells/dev/app/InspectableElements/InspectableElements.js
+++ b/shells/dev/app/InspectableElements/InspectableElements.js
@@ -1,6 +1,6 @@
// @flow
-import React, { Fragment } from 'react';
+import React from 'react';
import Contexts from './Contexts';
import CustomHooks from './CustomHooks';
import NestedProps from './NestedProps';
@@ -10,11 +10,11 @@ import styles from './InspectableElements.css';
export default function InspectableElements() {
return (
-
+
);
}
diff --git a/shells/dev/app/ToDoList/List.css b/shells/dev/app/ToDoList/List.css
index febdc684fa..ae39496000 100644
--- a/shells/dev/app/ToDoList/List.css
+++ b/shells/dev/app/ToDoList/List.css
@@ -1,3 +1,11 @@
+.App {
+ /* GitHub.com frontend fonts */
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
+ sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
+ font-size: 14px;
+ line-height: 1.5;
+}
+
.Header {
font-size: 1.5rem;
font-weight: bold;
diff --git a/shells/dev/app/ToDoList/List.js b/shells/dev/app/ToDoList/List.js
index 2c6e96b524..1ab1cac1f9 100644
--- a/shells/dev/app/ToDoList/List.js
+++ b/shells/dev/app/ToDoList/List.js
@@ -1,6 +1,6 @@
// @flow
-import React, { Fragment, useCallback, useState } from 'react';
+import React, { useCallback, useState } from 'react';
import ListItem from './ListItem';
import styles from './List.css';
@@ -77,7 +77,7 @@ export default function List(props: Props) {
);
return (
-
+
);
}
diff --git a/shells/dev/app/index.js b/shells/dev/app/index.js
index af1f5b8227..c04798ac80 100644
--- a/shells/dev/app/index.js
+++ b/shells/dev/app/index.js
@@ -1,19 +1,33 @@
/** @flow */
+// This test harness mounts each test app as a separate root to test multi-root applications.
+
import { createElement } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
-import App from './App';
+import ElementTypes from './ElementTypes';
+import InspectableElements from './InspectableElements';
+import ToDoList from './ToDoList';
-const container = document.createElement('div');
+const containers = [];
-((document.body: any): HTMLBodyElement).appendChild(container);
+function mountHelper(App) {
+ const container = document.createElement('div');
+
+ ((document.body: any): HTMLBodyElement).appendChild(container);
+
+ containers.push(container);
-function mountTestApp() {
render(createElement(App), container);
}
+function mountTestApp() {
+ mountHelper(ToDoList);
+ mountHelper(InspectableElements);
+ mountHelper(ElementTypes);
+}
+
function unmountTestApp() {
- unmountComponentAtNode(container);
+ containers.forEach(container => unmountComponentAtNode(container));
}
mountTestApp();
diff --git a/src/devtools/store.js b/src/devtools/store.js
index 6ef2c16739..59465fd3e4 100644
--- a/src/devtools/store.js
+++ b/src/devtools/store.js
@@ -53,6 +53,7 @@ export default class Store extends EventEmitter {
debug('constructor', 'subscribing to Bridge');
bridge.on('operations', this.onBridgeOperations);
+ bridge.on('shutdown', this.onBridgeShutdown);
}
get numElements(): number {
@@ -85,7 +86,9 @@ export default class Store extends EventEmitter {
for (let i = 0; i < this._roots.length; i++) {
rootID = this._roots[i];
root = ((this._idToElement.get(rootID): any): Element);
- if (rootWeight + root.weight > index) {
+ if (root.children.length === 0) {
+ continue;
+ } else if (rootWeight + root.weight > index) {
break;
} else {
rootWeight += root.weight;
@@ -96,7 +99,7 @@ export default class Store extends EventEmitter {
// Skip over the root itself, because roots aren't visible in the Elements tree.
const firstChildID = ((root: any): Element).children[0];
let currentElement = ((this._idToElement.get(firstChildID): any): Element);
- let currentWeight = 0;
+ let currentWeight = rootWeight;
while (index !== currentWeight) {
for (let i = 0; i < currentElement.children.length; i++) {
const childID = currentElement.children[i];
@@ -234,22 +237,28 @@ export default class Store extends EventEmitter {
if (parentID === 0) {
debug('Add', `new root fiber ${id}`);
- this._roots = this._roots.concat(id);
- this._rootIDToRendererID.set(id, rendererID);
+ if (this._idToElement.has(id)) {
+ // The renderer's tree walking approach sometimes mounts the same Fiber twice with Suspense and Lazy.
+ // For now, we avoid adding it to the tree twice by checking if it's already been mounted.
+ // Maybe in the future we'll revisit this.
+ } else {
+ this._roots = this._roots.concat(id);
+ this._rootIDToRendererID.set(id, rendererID);
- this._idToElement.set(id, {
- children: [],
- depth: -1,
- displayName: null,
- id,
- key: null,
- ownerID: 0,
- parentID: 0,
- type,
- weight: 0,
- });
+ this._idToElement.set(id, {
+ children: [],
+ depth: -1,
+ displayName: null,
+ id,
+ key: null,
+ ownerID: 0,
+ parentID: 0,
+ type,
+ weight: 0,
+ });
- haveRootsChanged = true;
+ haveRootsChanged = true;
+ }
} else {
ownerID = ((operations[i]: any): number);
i++;
@@ -313,8 +322,6 @@ export default class Store extends EventEmitter {
i = i + 2;
- debug('Remove', `fiber ${id} from tree`);
-
element = ((this._idToElement.get(id): any): Element);
parentID = element.parentID;
@@ -324,9 +331,13 @@ export default class Store extends EventEmitter {
parentElement = ((this._idToElement.get(parentID): any): Element);
if (parentElement == null) {
+ debug('Remove', `fiber ${id} root`);
+
this._roots = this._roots.filter(rootID => rootID !== id);
this._rootIDToRendererID.delete(id);
} else {
+ debug('Remove', `fiber ${id} from parent ${parentID}`);
+
parentElement.children = parentElement.children.filter(
childID => childID !== id
);
@@ -388,18 +399,42 @@ export default class Store extends EventEmitter {
this.emit('mutated', [addedElementIDs, removedElementIDs]);
};
+ onBridgeShutdown = () => {
+ debug('onBridgeShutdown', 'unsubscribing from Bridge');
+
+ bridge.off('operations', this.onBridgeOperations);
+ bridge.off('shutdown', this.onBridgeShutdown);
+ };
+
// DEBUG
- __printTree = (rootID: number) => {
- const printElement = (id: number) => {
- const element = ((this._idToElement.get(id): any): Element);
- console.log(
- `${'•'.repeat(element.depth)}${element.id}:${element.displayName ||
- ''}${element.key ? `key:"${element.key}"` : ''} (${element.weight})`
- );
- element.children.forEach(printElement);
- };
- const root = ((this._idToElement.get(rootID): any): Element);
- console.log('printing root:', rootID);
- root.children.forEach(printElement);
+ __printTree = () => {
+ console.group('__printTree()');
+ this._roots.forEach((rootID: number) => {
+ const printElement = (id: number) => {
+ const element = ((this._idToElement.get(id): any): Element);
+ console.log(
+ `${'•'.repeat(element.depth)}${element.id}:${element.displayName ||
+ ''}${element.key ? `key:"${element.key}"` : ''} (${element.weight})`
+ );
+ element.children.forEach(printElement);
+ };
+ const root = ((this._idToElement.get(rootID): any): Element);
+ console.group(`${rootID}:root (${root.weight})`);
+ root.children.forEach(printElement);
+ console.groupEnd();
+ });
+ console.group(`List of ${this.numElements} elements`);
+ for (let i = 0; i < this.numElements; i++) {
+ //if (i === 4) { debugger }
+ const element = this.getElementAtIndex(i);
+ if (element != null) {
+ console.log(
+ `${'•'.repeat(element.depth)}${i}: ${element.displayName ||
+ 'Unknown'}`
+ );
+ }
+ }
+ console.groupEnd();
+ console.groupEnd();
};
}