diff --git a/packages/react-devtools-extensions/src/contentScripts/backendManager.js b/packages/react-devtools-extensions/src/contentScripts/backendManager.js index 9d3ec414330ed..63519b506dd96 100644 --- a/packages/react-devtools-extensions/src/contentScripts/backendManager.js +++ b/packages/react-devtools-extensions/src/contentScripts/backendManager.js @@ -16,6 +16,7 @@ import {COMPACT_VERSION_NAME} from 'react-devtools-extensions/src/utils'; import {getIsReloadAndProfileSupported} from 'react-devtools-shared/src/utils'; let welcomeHasInitialized = false; +const requiredBackends = new Set(); function welcome(event: $FlowFixMe) { if ( @@ -49,8 +50,6 @@ function welcome(event: $FlowFixMe) { setup(window.__REACT_DEVTOOLS_GLOBAL_HOOK__); } -window.addEventListener('message', welcome); - function setup(hook: ?DevToolsHook) { // this should not happen, but Chrome can be weird sometimes if (hook == null) { @@ -71,20 +70,27 @@ function setup(hook: ?DevToolsHook) { updateRequiredBackends(); // register renderers that inject themselves later. - hook.sub('renderer', ({renderer}) => { + const unsubscribeRendererListener = hook.sub('renderer', ({renderer}) => { registerRenderer(renderer, hook); updateRequiredBackends(); }); // listen for backend installations. - hook.sub('devtools-backend-installed', version => { - activateBackend(version, hook); - updateRequiredBackends(); + const unsubscribeBackendInstallationListener = hook.sub( + 'devtools-backend-installed', + version => { + activateBackend(version, hook); + updateRequiredBackends(); + }, + ); + + const unsubscribeShutdownListener: () => void = hook.sub('shutdown', () => { + unsubscribeRendererListener(); + unsubscribeBackendInstallationListener(); + unsubscribeShutdownListener(); }); } -const requiredBackends = new Set(); - function registerRenderer(renderer: ReactRenderer, hook: DevToolsHook) { let version = renderer.reconcilerVersion || renderer.version; if (!hasAssignedBackend(version)) { @@ -139,6 +145,7 @@ function activateBackend(version: string, hook: DevToolsHook) { // If we received 'shutdown' from `agent`, we assume the `bridge` is already shutting down, // and that caused the 'shutdown' event on the `agent`, so we don't need to call `bridge.shutdown()` here. hook.emit('shutdown'); + delete window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__; }); initBackend(hook, agent, window, getIsReloadAndProfileSupported()); @@ -178,3 +185,13 @@ function updateRequiredBackends() { '*', ); } + +/* + * Make sure this is executed only once in case Frontend is reloaded multiple times while Backend is initializing + * We can't use `reactDevToolsAgent` field on a global Hook object, because it only cleaned up after both Frontend and Backend initialized + */ +if (!window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__) { + window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__ = true; + + window.addEventListener('message', welcome); +} diff --git a/packages/react-devtools-shared/src/backend/agent.js b/packages/react-devtools-shared/src/backend/agent.js index 88450fe29ebc9..3480c065bd8a9 100644 --- a/packages/react-devtools-shared/src/backend/agent.js +++ b/packages/react-devtools-shared/src/backend/agent.js @@ -754,6 +754,9 @@ export default class Agent extends EventEmitter<{ shutdown: () => void = () => { // Clean up the overlay if visible, and associated events. this.emit('shutdown'); + + this._bridge.removeAllListeners(); + this.removeAllListeners(); }; startProfiling: (recordChangeDescriptions: boolean) => void = diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index 9732bf105ca6c..c9d6922854902 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -3472,7 +3472,7 @@ export function attach( } function cleanup() { - // We don't patch any methods so there is no cleanup. + isProfiling = false; } function rootSupportsProfiling(root: any) { diff --git a/packages/react-devtools-shared/src/backend/index.js b/packages/react-devtools-shared/src/backend/index.js index 86714b7f61476..ace5dbc8c03ce 100644 --- a/packages/react-devtools-shared/src/backend/index.js +++ b/packages/react-devtools-shared/src/backend/index.js @@ -80,15 +80,12 @@ export function initBackend( }); hook.reactDevtoolsAgent = null; }; - agent.addListener('shutdown', onAgentShutdown); - subs.push(() => { - agent.removeListener('shutdown', onAgentShutdown); - }); + // Agent's event listeners are cleaned up by Agent in `shutdown` implementation. + agent.addListener('shutdown', onAgentShutdown); agent.addListener('updateHookSettings', settings => { hook.settings = settings; }); - agent.addListener('getHookSettings', () => { if (hook.settings != null) { agent.onHookSettings(hook.settings);