From 7608516479fb85bc40aa73d8a31a0c93397ee6ff Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Mon, 24 Jun 2024 13:28:17 +0100 Subject: [PATCH] refactor[react-devtools/extensions]: dont debounce cleanup logic on navigation (#30027) ## Summary There is a race condition in the way we poll if React is on the page and when we actually clear this polling instance. When user navigates to a different page, we will debounce a callback for 500ms, which will: 1. Cleanup previous React polling instance 2. Start a new React polling instance Since the cleanup logic is debounced, there is a small chance that by the time we are going to clean up this polling instance, it will be `eval`-ed on the page, that is using React. For example, when user is navigating from the page which doesn't have React running, to a page that has React running. Next, we incorrectly will try to mount React DevTools panels twice, which will result into conflicts in the Store, and the error will be shown to the user ## How did you test this change? Since this is a race condition, it is hard to reproduce consistently, but you can try this flow: 1. Open a page that is using React, open browser DevTools and React DevTools components panel 2. Open a page that is NOT using React, like google.com, wait ~5 seconds until you see `"Looks like this page doesn't have React, or it hasn't been loaded yet"` message in RDT panel 3. Open a page that is using React, observe the error `"Uncaught Error: Cannot add node "1" because a node with that id is already in the Store."` Couldn't been able to reproduce this with these changes. --- .../react-devtools-extensions/src/main/index.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/react-devtools-extensions/src/main/index.js b/packages/react-devtools-extensions/src/main/index.js index 5422567db7..8ed84dd8fb 100644 --- a/packages/react-devtools-extensions/src/main/index.js +++ b/packages/react-devtools-extensions/src/main/index.js @@ -412,13 +412,19 @@ chrome.devtools.network.onNavigated.addListener(syncSavedPreferences); // into subscribing to the same events from Bridge and window multiple times // In this case, we will handle `operations` event twice or more and user will see // `Cannot add node "1" because a node with that id is already in the Store.` -const debouncedOnNavigatedListener = debounce(() => { +const debouncedMountReactDevToolsCallback = debounce( + mountReactDevToolsWhenReactHasLoaded, + 500, +); + +// Clean up everything, but start mounting React DevTools panels if user stays at this page +function onNavigatedToOtherPage() { performInTabNavigationCleanup(); - mountReactDevToolsWhenReactHasLoaded(); -}, 500); + debouncedMountReactDevToolsCallback(); +} // Cleanup previous page state and remount everything -chrome.devtools.network.onNavigated.addListener(debouncedOnNavigatedListener); +chrome.devtools.network.onNavigated.addListener(onNavigatedToOtherPage); // Should be emitted when browser DevTools are closed if (__IS_FIREFOX__) {