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

pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new fa8f2337eef Prevent lazy loading for cached plugins (#61207)
fa8f2337eef is described below

commit fa8f2337eef7b24392e0e9e47db0f8ba74e94bcc
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Thu Jan 29 17:51:23 2026 +0100

    Prevent lazy loading for cached plugins (#61207)
---
 .../src/airflow/ui/src/pages/ExternalView.tsx        |  6 ++++--
 .../src/airflow/ui/src/pages/ReactPlugin.tsx         | 20 +++++++++++++++-----
 2 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/airflow-core/src/airflow/ui/src/pages/ExternalView.tsx 
b/airflow-core/src/airflow/ui/src/pages/ExternalView.tsx
index a1c80cdf2f3..c74d9b25a4b 100644
--- a/airflow-core/src/airflow/ui/src/pages/ExternalView.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/ExternalView.tsx
@@ -18,7 +18,7 @@
  */
 import { Box } from "@chakra-ui/react";
 import { useTranslation } from "react-i18next";
-import { useParams } from "react-router-dom";
+import { useLocation, useParams } from "react-router-dom";
 
 import { usePluginServiceGetPlugins } from "openapi/queries";
 import { ProgressBar } from "src/components/ui";
@@ -32,6 +32,8 @@ export const ExternalView = () => {
   const { page } = useParams();
   const { data: pluginData, isLoading } = usePluginServiceGetPlugins();
 
+  const { pathname } = useLocation();
+
   const externalView =
     page === "legacy-fab-views"
       ? {
@@ -82,7 +84,7 @@ export const ExternalView = () => {
         m={-2} // Compensate for parent padding
         minHeight={0}
       >
-        <ReactPlugin reactApp={reactApp} />
+        <ReactPlugin key={pathname} reactApp={reactApp} />
       </Box>
     );
   }
diff --git a/airflow-core/src/airflow/ui/src/pages/ReactPlugin.tsx 
b/airflow-core/src/airflow/ui/src/pages/ReactPlugin.tsx
index 9f43f72d90d..c530409a0e5 100644
--- a/airflow-core/src/airflow/ui/src/pages/ReactPlugin.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/ReactPlugin.tsx
@@ -59,19 +59,29 @@ const loadPlugin = (reactApp: ReactAppResponse): Promise<{ 
default: PluginCompon
       // eslint-disable-next-line no-console
       console.error("Component failed to load:", error);
 
-      return {
-        default: ErrorPage,
-      };
+      return { default: ErrorPage };
     });
 
 export const ReactPlugin = ({ reactApp }: { readonly reactApp: 
ReactAppResponse }) => {
   const { dagId, mapIndex, runId, taskId } = useParams();
 
-  const Plugin = lazy(() => loadPlugin(reactApp));
+  // If the plugin component was already registered on the global object by a 
previous load,
+  // render it directly without going through Suspense/lazy (avoids flashing 
the spinner).
+  const existing = (globalThis as Record<string, unknown>)[reactApp.name];
+
+  if (typeof existing === "function") {
+    const Plugin = existing as PluginComponentType;
+
+    return <Plugin dagId={dagId} mapIndex={mapIndex} runId={runId} 
taskId={taskId} />;
+  }
+
+  // Otherwise, lazy-load the bundle once. When it resolves, it must set a 
function component
+  // under globalThis[reactApp.name], which we then use as the default export.
+  const LazyPlugin = lazy(() => loadPlugin(reactApp));
 
   return (
     <Suspense fallback={<Spinner />}>
-      <Plugin dagId={dagId} mapIndex={mapIndex} runId={runId} taskId={taskId} 
/>
+      <LazyPlugin dagId={dagId} mapIndex={mapIndex} runId={runId} 
taskId={taskId} />
     </Suspense>
   );
 };

Reply via email to