diff --git a/packages/react-devtools-shell/index.html b/packages/react-devtools-shell/index.html index 81b9c2ce18ccc..4cde55278a9a9 100644 --- a/packages/react-devtools-shell/index.html +++ b/packages/react-devtools-shell/index.html @@ -51,7 +51,10 @@ multi DevTools | e2e tests + | e2e regression tests + | + perf regression tests diff --git a/packages/react-devtools-shell/perf-regression.html b/packages/react-devtools-shell/perf-regression.html new file mode 100644 index 0000000000000..27fd87f88dd8a --- /dev/null +++ b/packages/react-devtools-shell/perf-regression.html @@ -0,0 +1,45 @@ + + + + + React DevTools + + + + + +
+ +
+ + + \ 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.

+ + +
+ ); +} 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'),