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.
Ruslan Lesiutin committed
May 12, 2023 at 14:59 UTC
67a05d03e38b9837e27c9fe0a673557e63ff03c5
2 files changed
+14
-4
packages/react-devtools-extensions/src/backend.js
+1
@@ -29,5 +29,6 @@ function setup(hook: ?DevToolsHook) {
29
initBackend,
30
setupNativeStyleEditor,
31
});
32
+
33
hook.emit('devtools-backend-installed', COMPACT_VERSION_NAME);
34
}
packages/react-devtools-extensions/src/backendManager.js
+13
-4
@@ -58,7 +58,7 @@ function setup(hook: ?DevToolsHook) {
58
59
// register renderers that have already injected themselves.
60
hook.renderers.forEach(renderer => {
61
- registerRenderer(renderer);
61
+ registerRenderer(renderer, hook);
62
});
63
64
// Activate and remove from required all present backends, registered within the hook
@@ -71,7 +71,7 @@ function setup(hook: ?DevToolsHook) {
71
72
// register renderers that inject themselves later.
73
hook.sub('renderer', ({renderer}) => {
74
- registerRenderer(renderer);
74
+ registerRenderer(renderer, hook);
75
updateRequiredBackends();
76
});
77
@@ -84,12 +84,16 @@ function setup(hook: ?DevToolsHook) {
84
85
const requiredBackends = new Set<string>();
86
87
-function registerRenderer(renderer: ReactRenderer) {
87
+function registerRenderer(renderer: ReactRenderer, hook: DevToolsHook) {
88
let version = renderer.reconcilerVersion || renderer.version;
89
if (!hasAssignedBackend(version)) {
90
version = COMPACT_VERSION_NAME;
91
}
92
- requiredBackends.add(version);
92
+
93
+ // Check if required backend is already activated, no need to require again
94
+ if (!hook.backends.has(version)) {
95
+ requiredBackends.add(version);
96
+ }
97
}
98
99
function activateBackend(version: string, hook: DevToolsHook) {
@@ -97,6 +101,7 @@ function activateBackend(version: string, hook: DevToolsHook) {
101
if (!backend) {
102
throw new Error(`Could not find backend for version "${version}"`);
103
}
104
+
105
const {Agent, Bridge, initBackend, setupNativeStyleEditor} = backend;
106
const bridge = new Bridge({
107
listen(fn) {
@@ -157,6 +162,10 @@ function activateBackend(version: string, hook: DevToolsHook) {
162
163
// tell the service worker which versions of backends are needed for the current page
164
function updateRequiredBackends() {
165
+ if (requiredBackends.size === 0) {
166
+ return;
167
+ }
168
+
169
window.postMessage(
170
{
171
source: 'react-devtools-backend-manager',