+
+
+
\ No newline at end of file
diff --git a/packages/react-devtools-shell/src/e2e-regression/devtools.js b/packages/react-devtools-shell/src/e2e-regression/devtools.js
index d8554eb558657..eb3a62622415f 100644
--- a/packages/react-devtools-shell/src/e2e-regression/devtools.js
+++ b/packages/react-devtools-shell/src/e2e-regression/devtools.js
@@ -9,7 +9,7 @@ import {initialize as createDevTools} from 'react-devtools-inline/frontend';
// This is a pretty gross hack to make the runtime loaded named-hooks-code work.
// TODO (Webpack 5) Hoepfully we can remove this once we upgrade to Webpack 5.
-// $FlowFixMer
+// $FlowFixMe
__webpack_public_path__ = '/dist/'; // eslint-disable-line no-undef
// TODO (Webpack 5) Hopefully we can remove this prop after the Webpack 5 migration.
diff --git a/packages/react-devtools-shell/src/perf-regression/app.js b/packages/react-devtools-shell/src/perf-regression/app.js
new file mode 100644
index 0000000000000..88308e0fcf345
--- /dev/null
+++ b/packages/react-devtools-shell/src/perf-regression/app.js
@@ -0,0 +1,22 @@
+/** @flow */
+
+// This test harness mounts each test app as a separate root to test multi-root applications.
+
+import * as React from 'react';
+import {createRoot} from 'react-dom/client';
+import App from './apps/index';
+
+function mountApp() {
+ const container = document.createElement('div');
+
+ ((document.body: any): HTMLBodyElement).appendChild(container);
+
+ const root = createRoot(container);
+ root.render(
+
+
+ ,
+ );
+}
+
+mountApp();
diff --git a/packages/react-devtools-shell/src/perf-regression/apps/LargeSubtree.js b/packages/react-devtools-shell/src/perf-regression/apps/LargeSubtree.js
new file mode 100644
index 0000000000000..11da40cf2c111
--- /dev/null
+++ b/packages/react-devtools-shell/src/perf-regression/apps/LargeSubtree.js
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+import * as React from 'react';
+
+function generateArray(size) {
+ return Array.from({length: size}, () => Math.floor(Math.random() * size));
+}
+
+const arr = generateArray(50000);
+
+export default function LargeSubtree() {
+ const [showList, setShowList] = React.useState(false);
+ const toggleList = () => {
+ const startTime = performance.now();
+ setShowList(!showList);
+ // requestAnimationFrame should happen after render+commit is done
+ window.requestAnimationFrame(() => {
+ const afterRenderTime = performance.now();
+ console.log(
+ `Time spent on ${
+ showList ? 'unmounting' : 'mounting'
+ } the subtree: ${afterRenderTime - startTime}ms`,
+ );
+ });
+ };
+ return (
+
+
Mount/Unmount a large subtree
+
Click the button to toggle the state. Open console for results.
+
+
+
dummy item
+ {showList && arr.map((num, idx) =>
{num}
)}
+
+
+ );
+}
diff --git a/packages/react-devtools-shell/src/perf-regression/apps/index.js b/packages/react-devtools-shell/src/perf-regression/apps/index.js
new file mode 100644
index 0000000000000..2508dd40103ea
--- /dev/null
+++ b/packages/react-devtools-shell/src/perf-regression/apps/index.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+import * as React from 'react';
+import LargeSubtree from './LargeSubtree';
+
+export default function Home() {
+ return (
+
+
+
+ );
+}
diff --git a/packages/react-devtools-shell/src/perf-regression/devtools.js b/packages/react-devtools-shell/src/perf-regression/devtools.js
new file mode 100644
index 0000000000000..ffbc8afd718ce
--- /dev/null
+++ b/packages/react-devtools-shell/src/perf-regression/devtools.js
@@ -0,0 +1,55 @@
+import * as React from 'react';
+import {createRoot} from 'react-dom/client';
+import {
+ activate as activateBackend,
+ initialize as initializeBackend,
+} from 'react-devtools-inline/backend';
+import {initialize as createDevTools} from 'react-devtools-inline/frontend';
+
+// This is a pretty gross hack to make the runtime loaded named-hooks-code work.
+// TODO (Webpack 5) Hoepfully we can remove this once we upgrade to Webpack 5.
+// $FlowFixMe
+__webpack_public_path__ = '/dist/'; // eslint-disable-line no-undef
+
+// TODO (Webpack 5) Hopefully we can remove this prop after the Webpack 5 migration.
+function hookNamesModuleLoaderFunction() {
+ return import('react-devtools-inline/hookNames');
+}
+
+function inject(contentDocument, sourcePath) {
+ const script = contentDocument.createElement('script');
+ script.src = sourcePath;
+
+ ((contentDocument.body: any): HTMLBodyElement).appendChild(script);
+}
+
+function init(
+ appSource: string,
+ appIframe: HTMLIFrameElement,
+ devtoolsContainer: HTMLElement,
+ loadDevToolsButton: HTMLButtonElement,
+) {
+ const {contentDocument, contentWindow} = appIframe;
+
+ initializeBackend(contentWindow);
+
+ inject(contentDocument, appSource);
+
+ loadDevToolsButton.addEventListener('click', () => {
+ const DevTools = createDevTools(contentWindow);
+ createRoot(devtoolsContainer).render(
+ ,
+ );
+ activateBackend(contentWindow);
+ });
+}
+
+init(
+ 'dist/perf-regression-app.js',
+ document.getElementById('iframe'),
+ document.getElementById('devtools'),
+ document.getElementById('load-devtools'),
+);
diff --git a/packages/react-devtools-shell/webpack.config.js b/packages/react-devtools-shell/webpack.config.js
index 26d27a08a7cf6..e1a30f7334448 100644
--- a/packages/react-devtools-shell/webpack.config.js
+++ b/packages/react-devtools-shell/webpack.config.js
@@ -157,6 +157,8 @@ const app = makeConfig(
'multi-devtools': './src/multi/devtools.js',
'multi-right': './src/multi/right.js',
'e2e-regression': './src/e2e-regression/app.js',
+ 'perf-regression-app': './src/perf-regression/app.js',
+ 'perf-regression-devtools': './src/perf-regression/devtools.js',
},
{
react: resolve(builtModulesDir, 'react'),