From 316bf561ad7267f3eac2e51fe09adfc77c035ec5 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Wed, 11 Sep 2024 14:17:27 +0100 Subject: [PATCH] refactor[Agent/Store]: Store to send messages only after Agent is initialized --- .../src/backend/agent.js | 17 +++----- packages/react-devtools-shared/src/bridge.js | 1 + .../src/devtools/store.js | 41 +++++++++++-------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/agent.js b/packages/react-devtools-shared/src/backend/agent.js index 92db4c062d5a2..03ace4add2417 100644 --- a/packages/react-devtools-shared/src/backend/agent.js +++ b/packages/react-devtools-shared/src/backend/agent.js @@ -230,18 +230,16 @@ export default class Agent extends EventEmitter<{ bridge.addListener('overrideProps', this.overrideProps); bridge.addListener('overrideState', this.overrideState); + setupHighlighter(bridge, this); + setupTraceUpdates(this); + + // By this time, Store should already be initialized and intercept events + bridge.send('backendInitialized'); + if (this._isProfiling) { bridge.send('profilingStatus', true); } - // Send the Bridge protocol and backend versions, after initialization, in case the frontend has already requested it. - // The Store may be instantiated beore the agent. - const version = process.env.DEVTOOLS_VERSION; - if (version) { - this._bridge.send('backendVersion', version); - } - this._bridge.send('bridgeProtocol', currentBridgeProtocol); - // Notify the frontend if the backend supports the Storage API (e.g. localStorage). // If not, features like reload-and-profile will not work correctly and must be disabled. let isBackendStorageAPISupported = false; @@ -251,9 +249,6 @@ export default class Agent extends EventEmitter<{ } catch (error) {} bridge.send('isBackendStorageAPISupported', isBackendStorageAPISupported); bridge.send('isSynchronousXHRSupported', isSynchronousXHRSupported()); - - setupHighlighter(bridge, this); - setupTraceUpdates(this); } get rendererInterfaces(): {[key: RendererID]: RendererInterface, ...} { diff --git a/packages/react-devtools-shared/src/bridge.js b/packages/react-devtools-shared/src/bridge.js index 1e9b3c222d623..9e8bd71b71758 100644 --- a/packages/react-devtools-shared/src/bridge.js +++ b/packages/react-devtools-shared/src/bridge.js @@ -178,6 +178,7 @@ type SavedPreferencesParams = { }; export type BackendEvents = { + backendInitialized: [], backendVersion: [string], bridgeProtocol: [BridgeProtocol], extensionBackendInitialized: [], diff --git a/packages/react-devtools-shared/src/devtools/store.js b/packages/react-devtools-shared/src/devtools/store.js index ef6f720346246..e4d4b9dcceec0 100644 --- a/packages/react-devtools-shared/src/devtools/store.js +++ b/packages/react-devtools-shared/src/devtools/store.js @@ -191,6 +191,8 @@ export default class Store extends EventEmitter<{ // Used for windowing purposes. _weightAcrossRoots: number = 0; + _shouldCheckBridgeProtocolCompatibility: boolean = false; + constructor(bridge: FrontendBridge, config?: Config) { super(); @@ -218,6 +220,7 @@ export default class Store extends EventEmitter<{ supportsReloadAndProfile, supportsTimeline, supportsTraceUpdates, + checkBridgeProtocolCompatibility, } = config; if (supportsInspectMatchingDOMElement) { this._supportsInspectMatchingDOMElement = true; @@ -234,6 +237,9 @@ export default class Store extends EventEmitter<{ if (supportsTraceUpdates) { this._supportsTraceUpdates = true; } + if (checkBridgeProtocolCompatibility) { + this._shouldCheckBridgeProtocolCompatibility = true; + } } this._bridge = bridge; @@ -262,24 +268,9 @@ export default class Store extends EventEmitter<{ this._profilerStore = new ProfilerStore(bridge, this, isProfiling); - // Verify that the frontend version is compatible with the connected backend. - // See github.com/facebook/react/issues/21326 - if (config != null && config.checkBridgeProtocolCompatibility) { - // Older backends don't support an explicit bridge protocol, - // so we should timeout eventually and show a downgrade message. - this._onBridgeProtocolTimeoutID = setTimeout( - this.onBridgeProtocolTimeout, - 10000, - ); - - bridge.addListener('bridgeProtocol', this.onBridgeProtocol); - bridge.send('getBridgeProtocol'); - } - bridge.addListener('backendVersion', this.onBridgeBackendVersion); - bridge.send('getBackendVersion'); - bridge.addListener('saveToClipboard', this.onSaveToClipboard); + bridge.addListener('backendInitialized', this.onBackendInitialized); } // This is only used in tests to avoid memory leaks. @@ -1493,6 +1484,24 @@ export default class Store extends EventEmitter<{ copy(text); }; + onBackendInitialized: () => void = () => { + // Verify that the frontend version is compatible with the connected backend. + // See github.com/facebook/react/issues/21326 + if (this._shouldCheckBridgeProtocolCompatibility) { + // Older backends don't support an explicit bridge protocol, + // so we should timeout eventually and show a downgrade message. + this._onBridgeProtocolTimeoutID = setTimeout( + this.onBridgeProtocolTimeout, + 10000, + ); + + this._bridge.addListener('bridgeProtocol', this.onBridgeProtocol); + this._bridge.send('getBridgeProtocol'); + } + + this._bridge.send('getBackendVersion'); + }; + // The Store should never throw an Error without also emitting an event. // Otherwise Store errors will be invisible to users, // but the downstream errors they cause will be reported as bugs.