From efbe7dec6a4facaa5dc18bc7452aee1a622d379e Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 12 May 2023 14:59:53 +0100 Subject: [PATCH] fix[devtools]: fixed duplicated backend activation with multiple renderers (#26807) ## Summary Initially reported in https://github.com/facebook/react/issues/26797. Was not able to reproduce the exact same problem, but found this case: 1. Open corresponding codepen from the issue in debug mode 2. Open components tab of the extension 3. Refresh the page Received multiple errors: - Warning in the Console tab: Invalid renderer id "2". - Error in the Components tab: Uncaught Error: Cannot add node "3" because a node with that id is already in the Store. This problem has occurred after landing a fix in https://github.com/facebook/react/pull/26779. Looks like Chrome is keeping the injected scripts (the backend in this case) and we start backend twice. --- .../react-devtools-extensions/src/backend.js | 1 + .../src/backendManager.js | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/react-devtools-extensions/src/backend.js b/packages/react-devtools-extensions/src/backend.js index b79e1916518e4..c7947da96310f 100644 --- a/packages/react-devtools-extensions/src/backend.js +++ b/packages/react-devtools-extensions/src/backend.js @@ -29,5 +29,6 @@ function setup(hook: ?DevToolsHook) { initBackend, setupNativeStyleEditor, }); + hook.emit('devtools-backend-installed', COMPACT_VERSION_NAME); } diff --git a/packages/react-devtools-extensions/src/backendManager.js b/packages/react-devtools-extensions/src/backendManager.js index 13b3eedde787a..a77ca2f9601c6 100644 --- a/packages/react-devtools-extensions/src/backendManager.js +++ b/packages/react-devtools-extensions/src/backendManager.js @@ -58,7 +58,7 @@ function setup(hook: ?DevToolsHook) { // register renderers that have already injected themselves. hook.renderers.forEach(renderer => { - registerRenderer(renderer); + registerRenderer(renderer, hook); }); // Activate and remove from required all present backends, registered within the hook @@ -71,7 +71,7 @@ function setup(hook: ?DevToolsHook) { // register renderers that inject themselves later. hook.sub('renderer', ({renderer}) => { - registerRenderer(renderer); + registerRenderer(renderer, hook); updateRequiredBackends(); }); @@ -84,12 +84,16 @@ function setup(hook: ?DevToolsHook) { const requiredBackends = new Set(); -function registerRenderer(renderer: ReactRenderer) { +function registerRenderer(renderer: ReactRenderer, hook: DevToolsHook) { let version = renderer.reconcilerVersion || renderer.version; if (!hasAssignedBackend(version)) { version = COMPACT_VERSION_NAME; } - requiredBackends.add(version); + + // Check if required backend is already activated, no need to require again + if (!hook.backends.has(version)) { + requiredBackends.add(version); + } } function activateBackend(version: string, hook: DevToolsHook) { @@ -97,6 +101,7 @@ function activateBackend(version: string, hook: DevToolsHook) { if (!backend) { throw new Error(`Could not find backend for version "${version}"`); } + const {Agent, Bridge, initBackend, setupNativeStyleEditor} = backend; const bridge = new Bridge({ listen(fn) { @@ -157,6 +162,10 @@ function activateBackend(version: string, hook: DevToolsHook) { // tell the service worker which versions of backends are needed for the current page function updateRequiredBackends() { + if (requiredBackends.size === 0) { + return; + } + window.postMessage( { source: 'react-devtools-backend-manager',