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

kaxilnaik pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit bb53eedfddab0bebe06d66e25360f86847a33691
Author: Guan Ming(Wesley) Chiu <[email protected]>
AuthorDate: Wed Sep 10 01:41:00 2025 +0800

    Optimize Gantt group expansion with debouncing and deferred rendering 
(#55334)
---
 .../src/context/openGroups/OpenGroupsProvider.tsx  | 32 ++++++++++++++++++----
 .../airflow/ui/src/layouts/Details/Gantt/Gantt.tsx |  8 ++++--
 2 files changed, 33 insertions(+), 7 deletions(-)

diff --git 
a/airflow-core/src/airflow/ui/src/context/openGroups/OpenGroupsProvider.tsx 
b/airflow-core/src/airflow/ui/src/context/openGroups/OpenGroupsProvider.tsx
index 1fde16745c3..c65e9a9e1c3 100644
--- a/airflow-core/src/airflow/ui/src/context/openGroups/OpenGroupsProvider.tsx
+++ b/airflow-core/src/airflow/ui/src/context/openGroups/OpenGroupsProvider.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { useCallback, useMemo, useEffect, type PropsWithChildren } from 
"react";
+import { useDebouncedCallback } from "use-debounce";
 import { useLocalStorage } from "usehooks-ts";
 
 import { useStructureServiceStructureData } from "openapi/queries";
@@ -59,20 +60,41 @@ export const OpenGroupsProvider = ({ children, dagId }: 
Props) => {
     }
   }, [structure.nodes, allGroupIds, setAllGroupIds]);
 
+  const debouncedSetOpenGroupIds = useDebouncedCallback(
+    (newGroupIds: Array<string>) => {
+      setOpenGroupIds(newGroupIds);
+    },
+    100, // 100ms debounce for batch operations
+  );
+
   const toggleGroupId = useCallback(
     (groupId: string) => {
       if (openGroupIds.includes(groupId)) {
-        setOpenGroupIds(openGroupIds.filter((id) => id !== groupId));
+        debouncedSetOpenGroupIds(openGroupIds.filter((id) => id !== groupId));
       } else {
-        setOpenGroupIds([...openGroupIds, groupId]);
+        debouncedSetOpenGroupIds([...openGroupIds, groupId]);
       }
     },
-    [openGroupIds, setOpenGroupIds],
+    [openGroupIds, debouncedSetOpenGroupIds],
+  );
+
+  const setOpenGroupIdsImmediate = useCallback(
+    (newGroupIds: Array<string>) => {
+      debouncedSetOpenGroupIds.cancel();
+      setOpenGroupIds(newGroupIds);
+    },
+    [debouncedSetOpenGroupIds, setOpenGroupIds],
   );
 
   const value = useMemo<OpenGroupsContextType>(
-    () => ({ allGroupIds, openGroupIds, setAllGroupIds, setOpenGroupIds, 
toggleGroupId }),
-    [allGroupIds, openGroupIds, setAllGroupIds, setOpenGroupIds, 
toggleGroupId],
+    () => ({
+      allGroupIds,
+      openGroupIds,
+      setAllGroupIds,
+      setOpenGroupIds: setOpenGroupIdsImmediate,
+      toggleGroupId,
+    }),
+    [allGroupIds, openGroupIds, setAllGroupIds, setOpenGroupIdsImmediate, 
toggleGroupId],
   );
 
   return <OpenGroupsContext.Provider 
value={value}>{children}</OpenGroupsContext.Provider>;
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
index 3b6bdceb2da..004272fa4e6 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
@@ -33,7 +33,7 @@ import {
 import "chart.js/auto";
 import "chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm";
 import annotationPlugin from "chartjs-plugin-annotation";
-import { useMemo, useRef } from "react";
+import { useMemo, useRef, useDeferredValue } from "react";
 import { Bar } from "react-chartjs-2";
 import { useTranslation } from "react-i18next";
 import { useParams, useNavigate, useLocation } from "react-router-dom";
@@ -77,6 +77,7 @@ const MIN_BAR_WIDTH = 10;
 export const Gantt = ({ limit }: Props) => {
   const { dagId = "", groupId: selectedGroupId, runId = "", taskId: 
selectedTaskId } = useParams();
   const { openGroupIds } = useOpenGroups();
+  const deferredOpenGroupIds = useDeferredValue(openGroupIds);
   const { t: translate } = useTranslation("common");
   const { selectedTimezone } = useTimezone();
   const { colorMode } = useColorMode();
@@ -119,7 +120,10 @@ export const Gantt = ({ limit }: Props) => {
     },
   );
 
-  const { flatNodes } = useMemo(() => flattenNodes(dagStructure, 
openGroupIds), [dagStructure, openGroupIds]);
+  const { flatNodes } = useMemo(
+    () => flattenNodes(dagStructure, deferredOpenGroupIds),
+    [dagStructure, deferredOpenGroupIds],
+  );
 
   const isLoading = runsLoading || structureLoading || summariesLoading || 
tiLoading;
 

Reply via email to