This is an automated email from the ASF dual-hosted git repository.

msyavuz pushed a commit to branch msyavuz/fix/infinite-refresh-bug
in repository https://gitbox.apache.org/repos/asf/superset.git

commit d983c0dc40f18121b51d6ad50065b717e82cf083
Author: Mehmet Salih Yavuz <[email protected]>
AuthorDate: Fri Jan 9 21:15:25 2026 +0300

    fix(Tabs): prevent infinite rerenders with nested tabs
---
 .../src/dashboard/actions/dashboardState.js        | 12 ++++++++++--
 .../components/gridComponents/Tab/Tab.jsx          | 22 ++++++++++++++++------
 2 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/superset-frontend/src/dashboard/actions/dashboardState.js 
b/superset-frontend/src/dashboard/actions/dashboardState.js
index 18b567e20f..afda5554b6 100644
--- a/superset-frontend/src/dashboard/actions/dashboardState.js
+++ b/superset-frontend/src/dashboard/actions/dashboardState.js
@@ -603,13 +603,21 @@ export function onRefresh(
   force = false,
   interval = 0,
   dashboardId,
+  isLazyLoad = false,
 ) {
   return dispatch => {
-    dispatch({ type: ON_REFRESH });
+    // Only dispatch ON_REFRESH for dashboard-level refreshes
+    // Skip it for lazy-loaded tabs to prevent infinite loops
+    if (!isLazyLoad) {
+      dispatch({ type: ON_REFRESH });
+    }
+    
     refreshCharts(chartList, force, interval, dashboardId, dispatch).then(
       () => {
         dispatch(onRefreshSuccess());
-        dispatch(onFiltersRefresh());
+        if (!isLazyLoad) {
+          dispatch(onFiltersRefresh());
+        }
       },
     );
   };
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.jsx
index 8902e84a46..a52e416a56 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.jsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Fragment, useCallback, memo, useEffect } from 'react';
+import { Fragment, useCallback, memo, useEffect, useRef } from 'react';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
 import { useDispatch, useSelector } from 'react-redux';
@@ -129,6 +129,9 @@ const Tab = props => {
     state => state.dashboardState.tabActivationTimes?.[props.id] || 0,
   );
   const dashboardInfo = useSelector(state => state.dashboardInfo);
+  
+  // Track which refresh we've already handled to prevent duplicates
+  const handledRefreshRef = useRef(null);
 
   useEffect(() => {
     if (props.renderType === RENDER_TAB_CONTENT && props.isComponentVisible) {
@@ -137,13 +140,20 @@ const Tab = props => {
         tabActivationTime &&
         lastRefreshTime > tabActivationTime
       ) {
-        const chartIds = getChartIdsFromComponent(props.id, dashboardLayout);
-        if (chartIds.length > 0) {
-          requestAnimationFrame(() => {
+        // Create a unique key for this specific refresh
+        const refreshKey = `${props.id}-${lastRefreshTime}`;
+        
+        // Only proceed if we haven't already handled this refresh
+        if (handledRefreshRef.current !== refreshKey) {
+          handledRefreshRef.current = refreshKey;
+          
+          const chartIds = getChartIdsFromComponent(props.id, dashboardLayout);
+          if (chartIds.length > 0) {
+            // Use lazy load flag to avoid updating global refresh time
             setTimeout(() => {
-              dispatch(onRefresh(chartIds, true, 0, dashboardInfo.id));
+              dispatch(onRefresh(chartIds, true, 0, dashboardInfo.id, true));
             }, CHART_MOUNT_DELAY);
-          });
+          }
         }
       }
     }

Reply via email to