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:15 UTC
(superset) branch react18-embedded-root created (now 88bedebd097)
This is an automated email from the ASF dual-hosted git repository.
msyavuz pushed a change to branch react18-embedded-root
in repository https://gitbox.apache.org/repos/asf/superset.git
at 88bedebd097 fix(embedded): prevent duplicate React root on rehandshake
This branch includes the following new commits:
new 88bedebd097 fix(embedded): prevent duplicate React root on rehandshake
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(superset) 01/01: fix(embedded): prevent duplicate React root on rehandshake
Posted by ms...@apache.org.
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');