diff --git a/superset-frontend/src/embedded/index.tsx b/superset-frontend/src/embedded/index.tsx
index a7437968678e..7b890272d802 100644
--- a/superset-frontend/src/embedded/index.tsx
+++ b/superset-frontend/src/embedded/index.tsx
@@ -18,7 +18,7 @@
*/
import 'src/public-path';
-import { lazy, Suspense } from 'react';
+import { lazy, StrictMode, Suspense, useEffect } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import { Global } from '@emotion/react';
@@ -68,18 +68,19 @@ const LazyDashboardPage = lazy(
const EmbededLazyDashboardPage = () => {
const uiConfig = useUiConfig();
+ const emitDataMasks = uiConfig?.emitDataMasks;
- // Emit data mask changes to the parent window
- if (uiConfig?.emitDataMasks) {
+ // Emit data mask changes to the parent window. Subscribing inside an effect
+ // (rather than during render) ensures the unsubscribe runs on unmount,
+ // including StrictMode's dev-mode double-mount cycle.
+ useEffect(() => {
+ if (!emitDataMasks) return undefined;
log('setting up Switchboard event emitter');
let previousDataMask = store.getState().dataMask;
- store.subscribe(() => {
- const currentState = store.getState();
- const currentDataMask = currentState.dataMask;
-
- // Only emit if the dataMask has changed
+ return store.subscribe(() => {
+ const currentDataMask = store.getState().dataMask;
if (previousDataMask !== currentDataMask) {
Switchboard.emit('observeDataMask', {
...currentDataMask,
@@ -88,7 +89,7 @@ const EmbededLazyDashboardPage = () => {
previousDataMask = currentDataMask;
}
});
- }
+ }, [emitDataMasks]);
return ;
};
@@ -196,7 +197,11 @@ function start() {
if (!root) {
root = createRoot(appMountPoint);
}
- root.render();
+ root.render(
+
+
+ ,
+ );
},
err => {
// something is most likely wrong with the guest token; reset the guard
diff --git a/superset-frontend/src/views/index.tsx b/superset-frontend/src/views/index.tsx
index e583f2bad863..456c1f09e924 100644
--- a/superset-frontend/src/views/index.tsx
+++ b/superset-frontend/src/views/index.tsx
@@ -18,6 +18,7 @@
*/
import 'src/public-path';
+import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { logging } from '@apache-superset/core/utils';
import initPreamble from 'src/preamble';
@@ -31,7 +32,11 @@ if (appMountPoint) {
await initPreamble();
} finally {
const { default: App } = await import(/* webpackMode: "eager" */ './App');
- root.render();
+ root.render(
+
+
+ ,
+ );
}
})().catch(err => {
logging.error('Unhandled error during app initialization', err);
diff --git a/superset-frontend/src/views/menu.tsx b/superset-frontend/src/views/menu.tsx
index dda01a8e27ef..5ba511c70326 100644
--- a/superset-frontend/src/views/menu.tsx
+++ b/superset-frontend/src/views/menu.tsx
@@ -20,6 +20,7 @@ import 'src/public-path';
// Menu App. Used in views that do not already include the Menu component in the layout.
// eg, backend rendered views
+import { StrictMode } from 'react';
import { Provider } from 'react-redux';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
@@ -45,24 +46,26 @@ const emotionCache = createCache({
});
const app = (
-
-
-
-
- ) =>
- querystring.stringify(object, { encode: false }),
- }}
- >
-
-
-
-
-
-
+
+
+
+
+
+ ) =>
+ querystring.stringify(object, { encode: false }),
+ }}
+ >
+
+
+
+
+
+
+
);
const menuMountPoint = document.getElementById('app-menu');