diff --git a/shells/dev/app/DeeplyNestedComponents/index.js b/shells/dev/app/DeeplyNestedComponents/index.js
new file mode 100644
index 0000000000..61deec3754
--- /dev/null
+++ b/shells/dev/app/DeeplyNestedComponents/index.js
@@ -0,0 +1,34 @@
+// @flow
+
+import React, { Fragment } from 'react';
+
+function wrapWithHoc(Component, index) {
+ function HOC() {
+ return ;
+ }
+ HOC.displayName = `HOC-${index}`;
+ return HOC;
+}
+
+function wrapWithNested(Component, times) {
+ for (let i = 0; i < times; i++) {
+ Component = wrapWithHoc(Component, i);
+ }
+
+ return Component;
+}
+
+function Nested() {
+ return
Deeply nested div
;
+}
+
+const DeeplyNested = wrapWithNested(Nested, 100);
+
+export default function DeeplyNestedComponents() {
+ return (
+
+ Deeply nested component
+
+
+ );
+}
diff --git a/shells/dev/app/EditableProps/EditableProps.js b/shells/dev/app/EditableProps/EditableProps.js
deleted file mode 100644
index c48a78dc8c..0000000000
--- a/shells/dev/app/EditableProps/EditableProps.js
+++ /dev/null
@@ -1,132 +0,0 @@
-// @flow
-
-import React, {
- createContext,
- Component,
- Fragment,
- useCallback,
- useDebugValue,
- useEffect,
- useReducer,
- useState,
-} from 'react';
-import styles from './EditableProps.css';
-
-const initialData = { foo: 'FOO', bar: 'BAR' };
-
-function reducer(state, action) {
- switch (action.type) {
- case 'swap':
- return { foo: state.bar, bar: state.foo };
- default:
- throw new Error();
- }
-}
-
-type StatefulFunctionProps = {| name: string |};
-
-function StatefulFunction({ name }: StatefulFunctionProps) {
- const [count, updateCount] = useState(0);
- const debouncedCount = useDebounce(count, 1000);
- const handleUpdateCountClick = useCallback(() => updateCount(count + 1), [
- count,
- ]);
-
- const [data, dispatch] = useReducer(reducer, initialData);
- const handleUpdateReducerClick = useCallback(
- () => dispatch({ type: 'swap' }),
- []
- );
-
- return (
-
- Name: {name}
-
-
-
-
- Reducer state: foo "{data.foo}", bar "{data.bar}"
-
-
-
-
-
- );
-}
-
-const BoolContext = createContext(true);
-BoolContext.displayName = 'BoolContext';
-
-type Props = {| name: string, toggle: boolean |};
-type State = {| cities: Array, state: string |};
-
-class StatefulClass extends Component {
- static contextType = BoolContext;
-
- state: State = {
- cities: ['San Francisco', 'San Jose'],
- state: 'California',
- };
-
- handleChange = ({ target }) =>
- this.setState({
- state: target.value,
- });
-
- render() {
- return (
-
- Name: {this.props.name}
- Toggle: {this.props.toggle ? 'true' : 'false'}
-
- State:
-
- Cities: {this.state.cities.join(', ')}
- Context: {this.context ? 'true' : 'false'}
-
- );
- }
-}
-
-export default function EditableProps() {
- return (
-
- );
-}
-
-// Below copied from https://usehooks.com/
-function useDebounce(value, delay) {
- // State and setters for debounced value
- const [debouncedValue, setDebouncedValue] = useState(value);
-
- // Show the value in DevTools
- useDebugValue(debouncedValue);
-
- useEffect(
- () => {
- // Update debounced value after delay
- const handler = setTimeout(() => {
- setDebouncedValue(value);
- }, delay);
-
- // Cancel the timeout if value changes (also on delay change or unmount)
- // This is how we prevent debounced value from updating if value is changed ...
- // .. within the delay period. Timeout gets cleared and restarted.
- return () => {
- clearTimeout(handler);
- };
- },
- [value, delay] // Only re-call effect if value or delay changes
- );
-
- return debouncedValue;
-}
-// Above copied from https://usehooks.com/
diff --git a/shells/dev/app/EditableProps/index.js b/shells/dev/app/EditableProps/index.js
index a57026743b..c86d717ee3 100644
--- a/shells/dev/app/EditableProps/index.js
+++ b/shells/dev/app/EditableProps/index.js
@@ -1,5 +1,131 @@
// @flow
-import EditableProps from './EditableProps';
+import React, {
+ createContext,
+ Component,
+ Fragment,
+ useCallback,
+ useDebugValue,
+ useEffect,
+ useReducer,
+ useState,
+} from 'react';
-export default EditableProps;
+const initialData = { foo: 'FOO', bar: 'BAR' };
+
+function reducer(state, action) {
+ switch (action.type) {
+ case 'swap':
+ return { foo: state.bar, bar: state.foo };
+ default:
+ throw new Error();
+ }
+}
+
+type StatefulFunctionProps = {| name: string |};
+
+function StatefulFunction({ name }: StatefulFunctionProps) {
+ const [count, updateCount] = useState(0);
+ const debouncedCount = useDebounce(count, 1000);
+ const handleUpdateCountClick = useCallback(() => updateCount(count + 1), [
+ count,
+ ]);
+
+ const [data, dispatch] = useReducer(reducer, initialData);
+ const handleUpdateReducerClick = useCallback(
+ () => dispatch({ type: 'swap' }),
+ []
+ );
+
+ return (
+
+ Name: {name}
+
+
+
+
+ Reducer state: foo "{data.foo}", bar "{data.bar}"
+
+
+
+
+
+ );
+}
+
+const BoolContext = createContext(true);
+BoolContext.displayName = 'BoolContext';
+
+type Props = {| name: string, toggle: boolean |};
+type State = {| cities: Array, state: string |};
+
+class StatefulClass extends Component {
+ static contextType = BoolContext;
+
+ state: State = {
+ cities: ['San Francisco', 'San Jose'],
+ state: 'California',
+ };
+
+ handleChange = ({ target }) =>
+ this.setState({
+ state: target.value,
+ });
+
+ render() {
+ return (
+
+ Name: {this.props.name}
+ Toggle: {this.props.toggle ? 'true' : 'false'}
+
+ State:
+
+ Cities: {this.state.cities.join(', ')}
+ Context: {this.context ? 'true' : 'false'}
+
+ );
+ }
+}
+
+export default function EditableProps() {
+ return (
+
+ Editable props
+
+
+ );
+}
+
+// Below copied from https://usehooks.com/
+function useDebounce(value, delay) {
+ // State and setters for debounced value
+ const [debouncedValue, setDebouncedValue] = useState(value);
+
+ // Show the value in DevTools
+ useDebugValue(debouncedValue);
+
+ useEffect(
+ () => {
+ // Update debounced value after delay
+ const handler = setTimeout(() => {
+ setDebouncedValue(value);
+ }, delay);
+
+ // Cancel the timeout if value changes (also on delay change or unmount)
+ // This is how we prevent debounced value from updating if value is changed ...
+ // .. within the delay period. Timeout gets cleared and restarted.
+ return () => {
+ clearTimeout(handler);
+ };
+ },
+ [value, delay] // Only re-call effect if value or delay changes
+ );
+
+ return debouncedValue;
+}
+// Above copied from https://usehooks.com/
diff --git a/shells/dev/app/InspectableElements/InspectableElements.css b/shells/dev/app/InspectableElements/InspectableElements.css
deleted file mode 100644
index b1ebb8ee71..0000000000
--- a/shells/dev/app/InspectableElements/InspectableElements.css
+++ /dev/null
@@ -1,13 +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;
-}
-
-.Header {
- font-size: 1.5rem;
- font-weight: bold;
- margin-bottom: 0.5rem;
-}
diff --git a/shells/dev/app/InspectableElements/InspectableElements.js b/shells/dev/app/InspectableElements/InspectableElements.js
index b7cbbf2937..d22fc29f52 100644
--- a/shells/dev/app/InspectableElements/InspectableElements.js
+++ b/shells/dev/app/InspectableElements/InspectableElements.js
@@ -1,20 +1,19 @@
// @flow
-import React from 'react';
+import React, { Fragment } from 'react';
import Contexts from './Contexts';
import CustomHooks from './CustomHooks';
import NestedProps from './NestedProps';
-import styles from './InspectableElements.css';
// TODO Add Immutable JS example
export default function InspectableElements() {
return (
-
-
Inspectable elements
+
+ Inspectable elements
-
+
);
}
diff --git a/shells/dev/app/ToDoList/List.css b/shells/dev/app/ToDoList/List.css
index ae39496000..0583402840 100644
--- a/shells/dev/app/ToDoList/List.css
+++ b/shells/dev/app/ToDoList/List.css
@@ -1,17 +1,3 @@
-.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;
- margin-bottom: 0.5rem;
-}
-
.Input {
font-size: 1rem;
padding: 0.25rem;
diff --git a/shells/dev/app/ToDoList/List.js b/shells/dev/app/ToDoList/List.js
index a3fe76f388..b8a2a4636a 100644
--- a/shells/dev/app/ToDoList/List.js
+++ b/shells/dev/app/ToDoList/List.js
@@ -1,6 +1,6 @@
// @flow
-import React, { useCallback, useState } from 'react';
+import React, { Fragment, useCallback, useState } from 'react';
import ListItem from './ListItem';
import styles from './List.css';
@@ -77,8 +77,8 @@ export default function List(props: Props) {
);
return (
-
+
);
}
diff --git a/shells/dev/app/index.js b/shells/dev/app/index.js
index 77a8093b42..ea813a28fe 100644
--- a/shells/dev/app/index.js
+++ b/shells/dev/app/index.js
@@ -4,11 +4,14 @@
import { createElement } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
+import DeeplyNestedComponents from './DeeplyNestedComponents';
import EditableProps from './EditableProps';
import ElementTypes from './ElementTypes';
import InspectableElements from './InspectableElements';
import ToDoList from './ToDoList';
+import './styles.css';
+
const containers = [];
function mountHelper(App) {
@@ -26,6 +29,7 @@ function mountTestApp() {
mountHelper(InspectableElements);
mountHelper(ElementTypes);
mountHelper(EditableProps);
+ mountHelper(DeeplyNestedComponents);
}
function unmountTestApp() {
diff --git a/shells/dev/app/EditableProps/EditableProps.css b/shells/dev/app/styles.css
similarity index 94%
rename from shells/dev/app/EditableProps/EditableProps.css
rename to shells/dev/app/styles.css
index b1ebb8ee71..2a995314c1 100644
--- a/shells/dev/app/EditableProps/EditableProps.css
+++ b/shells/dev/app/styles.css
@@ -1,4 +1,4 @@
-.App {
+body {
/* GitHub.com frontend fonts */
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
@@ -6,7 +6,7 @@
line-height: 1.5;
}
-.Header {
+h1 {
font-size: 1.5rem;
font-weight: bold;
margin-bottom: 0.5rem;
diff --git a/src/devtools/views/Element.js b/src/devtools/views/Element.js
index 8b0cebe6c3..c87b11b480 100644
--- a/src/devtools/views/Element.js
+++ b/src/devtools/views/Element.js
@@ -1,6 +1,13 @@
// @flow
-import React, { Fragment, useCallback, useContext, useEffect, useMemo, useRef } from 'react';
+import React, {
+ Fragment,
+ useCallback,
+ useContext,
+ useEffect,
+ useMemo,
+ useRef,
+} from 'react';
import { ElementTypeClass, ElementTypeFunction } from 'src/devtools/types';
import { createRegExp } from './utils';
import { TreeContext } from './TreeContext';
@@ -26,6 +33,7 @@ export default function ElementView({ index, style }: Props) {
const element = getElementAtIndex(index);
const id = element === null ? null : element.id;
+ const isSelected = selectedElementID === id;
const handleDoubleClick = useCallback(() => {
if (id !== null) {
@@ -33,7 +41,7 @@ export default function ElementView({ index, style }: Props) {
}
}, [id, selectOwner]);
- const ref = useRef();
+ const ref = useRef(null);
useEffect(() => {
if (isSelected) {
@@ -64,7 +72,6 @@ export default function ElementView({ index, style }: Props) {
const { depth, displayName, key, type } = ((element: any): Element);
- const isSelected = selectedElementID === id;
const showDollarR =
isSelected && (type === ElementTypeClass || type === ElementTypeFunction);