You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ms...@apache.org on 2026/05/04 16:41:16 UTC
(superset) 01/01: fix(embedded): prevent duplicate React root on rehandshake
This is an automated email from the ASF dual-hosted git repository.
msyavuz pushed a commit to branch react18-embedded-root
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 88bedebd0971f8739f667e8856d919d7a7b901be
Author: Mehmet Salih Yavuz <sa...@proton.me>
AuthorDate: Mon May 4 19:40:57 2026 +0300
fix(embedded): prevent duplicate React root on rehandshake
The `started` flag and the inline `createRoot(appMountPoint).render(...)`
call both lived inside the 'message' event handler, so each port-transfer
event reset `started` to false and could invoke `createRoot` a second time
on the same DOM node — which React 18 warns about and where behavior is
undefined.
Hoist `started` and a lazily-created `root` to module scope so that
re-handshakes reuse the existing root, and unmount it in the existing
`beforeunload` handler.
---
superset-frontend/src/embedded/index.tsx | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/superset-frontend/src/embedded/index.tsx b/superset-frontend/src/embedded/index.tsx
index 4d72d5815b4..4ea1162aa24 100644
--- a/superset-frontend/src/embedded/index.tsx
+++ b/superset-frontend/src/embedded/index.tsx
@@ -19,7 +19,7 @@
import 'src/public-path';
import { lazy, Suspense } from 'react';
-import { createRoot } from 'react-dom/client';
+import { createRoot, type Root } from 'react-dom/client';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import { Global } from '@emotion/react';
import { t } from '@apache-superset/core/translation';
@@ -150,6 +150,8 @@ if (!window.parent || window.parent === window) {
// }
let displayedUnauthorizedToast = false;
+let root: Root | null = null;
+let started = false;
/**
* If there is a problem with the guest token, we will start getting
@@ -189,7 +191,10 @@ function start() {
type: USER_LOADED,
user: result,
});
- createRoot(appMountPoint).render(<EmbeddedApp />);
+ if (!root) {
+ root = createRoot(appMountPoint);
+ }
+ root.render(<EmbeddedApp />);
},
err => {
// something is most likely wrong with the guest token
@@ -243,8 +248,6 @@ window.addEventListener('message', function embeddedPageInitializer(event) {
debug: debugMode,
});
- let started = false;
-
Switchboard.defineMethod(
'guestToken',
({ guestToken }: { guestToken: string }) => {
@@ -322,7 +325,7 @@ window.addEventListener('message', function embeddedPageInitializer(event) {
}
});
-// Clean up theme controller on page unload
+// Clean up theme controller and unmount React root on page unload
window.addEventListener('beforeunload', () => {
try {
const controller = getThemeController();
@@ -333,6 +336,10 @@ window.addEventListener('beforeunload', () => {
} catch (error) {
logging.warn('Failed to destroy theme controller:', error);
}
+ if (root) {
+ root.unmount();
+ root = null;
+ }
});
log('embed page is ready to receive messages');