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

arafat2198 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new c937649d46e HDDS-13677. Update Axios to 1.9.0 and improve error 
handling (#9025).
c937649d46e is described below

commit c937649d46e7df7949a43849b9942aa07ef6453b
Author: Abhishek Pal <[email protected]>
AuthorDate: Fri Oct 10 15:48:52 2025 +0530

    HDDS-13677. Update Axios to 1.9.0 and improve error handling (#9025).
---
 .../webapps/recon/ozone-recon-web/package.json     |   2 +-
 .../webapps/recon/ozone-recon-web/pnpm-lock.yaml   |   8 +-
 .../src/components/navBar/navBar.tsx               |   2 +-
 .../recon/ozone-recon-web/src/utils/common.tsx     |  48 +++--
 .../decommissioningSummary.tsx                     |  78 +++-----
 .../src/v2/components/navBar/navBar.tsx            |  38 +---
 .../src/v2/components/nuMetadata/nuMetadata.tsx    | 116 ++++++------
 .../src/v2/components/tables/containersTable.tsx   |  30 ++-
 .../tables/insights/containerMismatchTable.tsx     |  77 ++++----
 .../tables/insights/deletePendingDirsTable.tsx     |  67 ++++---
 .../tables/insights/deletePendingKeysTable.tsx     | 109 +++++------
 .../tables/insights/deletedContainerKeysTable.tsx  |  70 ++++---
 .../components/tables/insights/openKeysTable.tsx   |  88 ++++-----
 .../src/v2/hooks/useAPIData.hook.ts                | 204 ++++++++++++++-------
 .../src/v2/pages/buckets/buckets.tsx               | 159 ++++++++--------
 .../src/v2/pages/containers/containers.tsx         | 132 +++++++------
 .../src/v2/pages/datanodes/datanodes.tsx           | 143 +++++++--------
 .../src/v2/pages/heatmap/heatmap.tsx               | 175 ++++++++----------
 .../src/v2/pages/insights/insights.tsx             | 147 +++++++--------
 .../src/v2/pages/insights/omInsights.tsx           |   3 +-
 .../src/v2/pages/namespaceUsage/namespaceUsage.tsx |  82 ++++-----
 .../src/v2/pages/pipelines/pipelines.tsx           | 104 +++++------
 .../src/v2/pages/volumes/volumes.tsx               | 113 +++++-------
 .../ozone-recon-web/src/views/buckets/buckets.tsx  |   2 +-
 .../src/views/datanodes/datanodes.tsx              |   6 +-
 .../src/views/datanodes/decommissionSummary.tsx    |   2 +-
 .../src/views/diskUsage/diskUsage.tsx              |   8 +-
 .../src/views/insights/insights.tsx                |   6 +-
 .../ozone-recon-web/src/views/insights/om/om.tsx   |  18 +-
 .../views/missingContainers/missingContainers.tsx  |   4 +-
 .../src/views/overview/overview.tsx                |   4 +-
 .../src/views/pipelines/pipelines.tsx              |   2 +-
 .../ozone-recon-web/src/views/volumes/volumes.tsx  |   2 +-
 33 files changed, 1005 insertions(+), 1044 deletions(-)

diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/package.json
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/package.json
index cb749226cab..bdd0da326cf 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/package.json
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/package.json
@@ -16,7 +16,7 @@
     "ag-charts-community": "^7.3.0",
     "ag-charts-react": "^7.3.0",
     "antd": "~4.10.3",
-    "axios": "^0.30.2",
+    "axios": "~1.9.0",
     "classnames": "^2.3.2",
     "echarts": "^5.5.0",
     "filesize": "^6.4.0",
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/pnpm-lock.yaml
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/pnpm-lock.yaml
index 543ed58e36e..0223816ef04 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/pnpm-lock.yaml
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/pnpm-lock.yaml
@@ -21,8 +21,8 @@ dependencies:
     specifier: ~4.10.3
     version: 4.10.3([email protected])([email protected])
   axios:
-    specifier: ^0.30.2
-    version: 0.30.2
+    specifier: ~1.9.0
+    version: 1.9.0
   classnames:
     specifier: ^2.3.2
     version: 2.5.1
@@ -1908,8 +1908,8 @@ packages:
     resolution: {integrity: 
sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==}
     dev: true
 
-  /[email protected]:
-    resolution: {integrity: 
sha512-0pE4RQ4UQi1jKY6p7u6i1Tkzqmu+d+/tHS7Q7rKunWLB9WyilBTpHHpXzPNMDj5hTbK0B0PTLSz07yqMBiF6xg==}
+  /[email protected]:
+    resolution: {integrity: 
sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==}
     dependencies:
       follow-redirects: 1.15.11
       form-data: 4.0.4
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/navBar/navBar.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/navBar/navBar.tsx
index b0bdf187cb3..18d8fa70480 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/navBar/navBar.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/navBar/navBar.tsx
@@ -82,7 +82,7 @@ class NavBar extends React.Component<INavBarProps> {
       this.setState({
         isLoading: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   };
 
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx
index 59e4d180f44..e5509538328 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx
@@ -18,7 +18,7 @@
 
 import moment from 'moment';
 import { notification } from 'antd';
-import { CanceledError } from 'axios';
+import axios, { CanceledError, AxiosError } from 'axios';
 
 export const getCapacityPercent = (used: number, total: number) => 
Math.round((used / total) * 100);
 
@@ -43,16 +43,42 @@ export const showInfoNotification = (title: string, 
description: string) => {
   notification.warn(args);
 };
 
-export const showDataFetchError = (error: string) => {
+export const showDataFetchError = (error: string | AxiosError | unknown) => {
   let title = 'Error while fetching data';
+  let errorMessage = '';
   
-  if (error.includes('CanceledError')) return;
-  if (error.includes('metadata')) {
-    title = 'Metadata Initialization:';
-    showInfoNotification(title, error);
-    return;
+  // Handle AxiosError instances
+  if (axios.isAxiosError(error)) {
+    // Don't show notifications for canceled requests
+    if (error.code === 'ERR_CANCELED' || error.name === 'CanceledError') {
+      return;
+    }
+    
+    if (error.response) {
+      // Server responded with error status
+      errorMessage = `Server Error (${error.response.status}): 
${error.response.statusText}`;
+      if (error.response.data && typeof error.response.data === 'string') {
+        errorMessage += ` - ${error.response.data}`;
+      }
+    } else if (error.request) {
+      // Request was made but no response received
+      errorMessage = 'Network Error: No response received from server';
+    } else {
+      // Something else happened
+      errorMessage = error.message || 'Unknown error occurred';
+    }
+  } else {
+    errorMessage = error as string;
+    
+    if (errorMessage.includes('CanceledError')) return;
+    if (errorMessage.includes('metadata')) {
+      title = 'Metadata Initialization:';
+      showInfoNotification(title, errorMessage);
+      return;
+    }
   }
-  showErrorNotification(title, error);
+  
+  showErrorNotification(title, errorMessage);
 };
 
 export const byteToSize = (bytes: number, decimals: number) => {
@@ -106,12 +132,12 @@ export const checkResponseError = (responses: 
Awaited<Promise<any>>[]) => {
 
   if (responseError.length !== 0) {
     responseError.forEach((err) => {
-      if (err.reason.toString().includes("CanceledError")) {
+      if (err.reason instanceof CanceledError || err.reason.code === 
'ERR_CANCELED') {
         throw new CanceledError('canceled', "ERR_CANCELED");
       }
       else {
-        const reqMethod = err.reason.config.method;
-        const reqURL = err.reason.config.url
+        const reqMethod = err.reason.config?.method || 'unknown';
+        const reqURL = err.reason.config?.url || 'unknown URL';
         showDataFetchError(
           `Failed to ${reqMethod} URL ${reqURL}\n${err.reason.toString()}`
         );
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/decommissioningSummary/decommissioningSummary.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/decommissioningSummary/decommissioningSummary.tsx
index 34e72b0889a..ebf3ed67eba 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/decommissioningSummary/decommissioningSummary.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/decommissioningSummary/decommissioningSummary.tsx
@@ -16,22 +16,17 @@
  * limitations under the License.
  */
 
-import React, { useEffect } from 'react';
-import { AxiosError } from 'axios';
+import React from 'react';
 import { Descriptions, Popover, Result } from 'antd';
 import { SummaryData } from '@/v2/types/datanode.types';
-import { AxiosGetHelper, cancelRequests } from '@/utils/axiosRequestHelper';
 import { showDataFetchError } from '@/utils/common';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
 import Spin from 'antd/es/spin';
 
 type DecommisioningSummaryProps = {
   uuid: string;
 }
 
-type DecommisioningSummaryState = {
-  loading: boolean;
-  summaryData: SummaryData | Record<string, unknown>;
-};
 
 function getDescriptions(summaryData: SummaryData): React.ReactElement {
   const {
@@ -67,59 +62,34 @@ function getDescriptions(summaryData: SummaryData): 
React.ReactElement {
 const DecommissionSummary: React.FC<DecommisioningSummaryProps> = ({
   uuid = ''
 }) => {
-  const [state, setState] = React.useState<DecommisioningSummaryState>({
-    summaryData: {},
-    loading: false
-  });
-  const cancelSignal = React.useRef<AbortController>();
+  const { 
+    data: decommissionResponse, 
+    loading, 
+    error 
+  } = useApiData<{DatanodesDecommissionInfo: SummaryData[]}>(
+    `/api/v1/datanodes/decommission/info/datanode?uuid=${uuid}`,
+    { DatanodesDecommissionInfo: [] },
+    {
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+
+  const summaryData = decommissionResponse.DatanodesDecommissionInfo[0] || {};
+
   let content = (
     <Spin
       size='large'
       style={{ margin: '15px 15px 10px 15px' }} />
   );
 
-  async function fetchDecommissionSummary(selectedUuid: string) {
-    setState({
-      ...state,
-      loading: true
-    });
-    try {
-      const { request, controller } = AxiosGetHelper(
-        `/api/v1/datanodes/decommission/info/datanode?uuid=${selectedUuid}`,
-        cancelSignal.current
-      );
-      cancelSignal.current = controller;
-      const datanodesInfoResponse = await request;
-      setState({
-        ...state,
-        loading: false,
-        summaryData: datanodesInfoResponse?.data?.DatanodesDecommissionInfo[0] 
?? {}
-      });
-    } catch (error) {
-      setState({
-        ...state,
-        loading: false,
-        summaryData: {}
-      });
-      showDataFetchError((error as AxiosError).toString());
-      content = (
-        <Result
-          status='error'
-          title='Unable to fetch Decommission Summary data'
-          className='decommission-summary-result' />
-      )
-    }
-  }
-
-  useEffect(() => {
-    fetchDecommissionSummary(uuid);
-    return (() => {
-      cancelRequests([cancelSignal.current!]);
-    })
-  }, []);
-
-  const { summaryData } = state;
-  if (summaryData?.datanodeDetails
+  if (error) {
+    content = (
+      <Result
+        status='error'
+        title='Unable to fetch Decommission Summary data'
+        className='decommission-summary-result' />
+    );
+  } else if (summaryData?.datanodeDetails
       && summaryData?.metrics
       && summaryData?.containers
   ) {
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx
index 518afef0198..3cc6b2aca91 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/navBar/navBar.tsx
@@ -16,8 +16,7 @@
  * limitations under the License.
  */
 
-import React, {useEffect, useRef, useState} from 'react';
-import {AxiosResponse} from 'axios';
+import React, {useEffect} from 'react';
 import {Layout, Menu} from 'antd';
 import {
   BarChartOutlined,
@@ -36,7 +35,7 @@ import {Link, useLocation} from 'react-router-dom';
 
 import logo from '@/logo.png';
 import {showDataFetchError} from '@/utils/common';
-import {AxiosGetHelper, cancelRequests} from '@/utils/axiosRequestHelper';
+import {useApiData} from '@/v2/hooks/useAPIData.hook';
 
 import './navBar.less';
 
@@ -51,34 +50,17 @@ const NavBar: React.FC<NavBarProps> = ({
   collapsed = false,
   onCollapse = () => { }
 }) => {
-  const [isHeatmapEnabled, setIsHeatmapEnabled] = useState<boolean>(false);
-  const cancelDisabledFeatureSignal = useRef<AbortController>();
   const location = useLocation();
-
-  const fetchDisabledFeatures = async () => {
-    const disabledfeaturesEndpoint = `/api/v1/features/disabledFeatures`;
-    const { request, controller } = AxiosGetHelper(
-      disabledfeaturesEndpoint,
-      cancelDisabledFeatureSignal.current
-    )
-    cancelDisabledFeatureSignal.current = controller;
-    try {
-      const response: AxiosResponse<string[]> = await request;
-      const heatmapDisabled = response?.data?.includes('HEATMAP')
-      setIsHeatmapEnabled(!heatmapDisabled);
-    } catch (error: unknown) {
-      showDataFetchError((error as Error).toString())
+  
+  const { data: disabledFeatures, error } = useApiData<string[]>(
+    '/api/v1/features/disabledFeatures',
+    [],
+    {
+      onError: (error) => showDataFetchError(error)
     }
-  }
-
+  );
 
-  useEffect(() => {
-    fetchDisabledFeatures();
-    // Component will unmount
-    return (() => {
-      cancelRequests([cancelDisabledFeatureSignal.current!])
-    })
-  }, [])
+  const isHeatmapEnabled = !disabledFeatures.includes('HEATMAP');
 
   const menuItems = [(
     <Menu.Item key='/Overview'
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/nuMetadata/nuMetadata.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/nuMetadata/nuMetadata.tsx
index eeb7475e52a..bcea9ab40cf 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/nuMetadata/nuMetadata.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/nuMetadata/nuMetadata.tsx
@@ -16,13 +16,12 @@
  * limitations under the License.
  */
 
-import React, {useRef, useState} from 'react';
+import React, {useState, useEffect, useCallback} from 'react';
 import moment from 'moment';
-import axios, {AxiosError} from 'axios';
 import {Table} from 'antd';
 
-import {AxiosGetHelper, cancelRequests, PromiseAllSettledGetHelper} from 
'@/utils/axiosRequestHelper';
-import {byteToSize, checkResponseError, removeDuplicatesAndMerge, 
showDataFetchError} from '@/utils/common';
+import {byteToSize, removeDuplicatesAndMerge, showDataFetchError} from 
'@/utils/common';
+import {useApiData, fetchData} from '@/v2/hooks/useAPIData.hook';
 
 import {Acl} from '@/v2/types/acl.types';
 
@@ -124,12 +123,30 @@ type MetadataState = {
 const NUMetadata: React.FC<MetadataProps> = ({
   path = '/'
 }) => {
-  const [loading, setLoading] = useState<boolean>(false);
   const [state, setState] = useState<MetadataState>([]);
-  const keyMetadataSummarySignal = useRef<AbortController>();
-  const cancelMetadataSignal = useRef<AbortController>();
+  const [isProcessingData, setIsProcessingData] = useState<boolean>(false);
+  // Individual API calls that resolve together
+  const summaryAPI = useApiData<SummaryResponse>(
+    `/api/v1/namespace/summary?path=${path}`,
+    {} as SummaryResponse,
+    {
+      retryAttempts: 2,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+  
+  const quotaAPI = useApiData<any>(
+    `/api/v1/namespace/quota?path=${path}`,
+    {},
+    {
+      retryAttempts: 2,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+  
+  const loading = summaryAPI.loading || quotaAPI.loading || isProcessingData;
 
-  const getObjectInfoMapping = React.useCallback((summaryResponse) => {
+  const getObjectInfoMapping = useCallback((summaryResponse) => {
     const data: MetadataState = [];
     /**
      * We are creating a specific set of keys under Object Info response
@@ -230,25 +247,15 @@ const NUMetadata: React.FC<MetadataProps> = ({
     return data;
   }, [path]);
 
-  function loadData(path: string) {
-    const { requests, controller } = PromiseAllSettledGetHelper([
-      `/api/v1/namespace/summary?path=${path}`,
-      `/api/v1/namespace/quota?path=${path}`
-    ], cancelMetadataSignal.current);
-    cancelMetadataSignal.current = controller;
-
-    requests.then(axios.spread((
-      nsSummaryResponse: Awaited<Promise<any>>,
-      quotaApiResponse: Awaited<Promise<any>>,
-    ) => {
-      checkResponseError([nsSummaryResponse, quotaApiResponse]);
-      const summaryResponse: SummaryResponse = nsSummaryResponse.value?.data 
?? {};
-      const quotaResponse = quotaApiResponse.value?.data ?? {};
+  // Process data when both APIs complete
+  const processMetadata = useCallback(async (summaryResponse: SummaryResponse, 
quotaResponse: any) => {
+    setIsProcessingData(true);
+    try {
       let data: MetadataState = [];
       let summaryResponsePresent = true;
       let quotaResponsePresent = true;
 
-      // Error checks
+      // Error checks for summary response
       if (summaryResponse.status === 'INITIALIZING') {
         summaryResponsePresent = false;
         showDataFetchError(`The metadata is currently initializing. Please 
wait a moment and try again later`);
@@ -269,30 +276,27 @@ const NUMetadata: React.FC<MetadataProps> = ({
 
         // If the entity is a Key then fetch the Key metadata only
         if (summaryResponse.type === 'KEY') {
-          const { request: metadataRequest, controller: metadataNewController 
} = AxiosGetHelper(
-            `/api/v1/namespace/usage?path=${path}&replica=true`,
-            keyMetadataSummarySignal.current
-          );
-          keyMetadataSummarySignal.current = metadataNewController;
-          metadataRequest.then(response => {
+          try {
+            const usageResponse: any = await 
fetchData(`/api/v1/namespace/usage?path=${path}&replica=true`);
             data.push(...[{
               key: 'File Size',
-              value: byteToSize(response.data.size, 3)
+              value: byteToSize(usageResponse.size, 3)
             }, {
               key: 'File Size With Replication',
-              value: byteToSize(response.data.sizeWithReplica, 3)
+              value: byteToSize(usageResponse.sizeWithReplica, 3)
             }, {
               key: 'Creation Time',
               value: 
moment(summaryResponse.objectInfo.creationTime).format('ll LTS')
             }, {
               key: 'Modification Time',
               value: 
moment(summaryResponse.objectInfo.modificationTime).format('ll LTS')
-            }])
+            }]);
             setState(data);
-          }).catch(error => {
-            showDataFetchError(error.toString());
-          });
-          return;
+            return;
+          } catch (error) {
+            showDataFetchError(error);
+            return;
+          }
         }
 
         data = removeDuplicatesAndMerge(data, 
getObjectInfoMapping(summaryResponse), 'key');
@@ -307,7 +311,7 @@ const NUMetadata: React.FC<MetadataProps> = ({
           numBucket: 'Buckets',
           numDir: 'Total Directories',
           numKey: 'Total Keys'
-        }
+        };
         Object.keys(countStats).forEach((key: string) => {
           if (countStats[key as keyof CountStats] !== undefined
             && countStats[key as keyof CountStats] !== -1) {
@@ -316,9 +320,10 @@ const NUMetadata: React.FC<MetadataProps> = ({
               value: countStats[key as keyof CountStats]
             });
           }
-        })
+        });
       }
 
+      // Error checks for quota response
       if (quotaResponse.state === 'INITIALIZING') {
         quotaResponsePresent = false;
         showDataFetchError(`The quota is currently initializing. Please wait a 
moment and try again later`);
@@ -342,26 +347,27 @@ const NUMetadata: React.FC<MetadataProps> = ({
           data.push({
             key: 'Quota Used',
             value: byteToSize(quotaResponse.used, 3)
-          })
+          });
         }
       }
+      
       setState(data);
-    })).catch(error => {
-      showDataFetchError((error as AxiosError).toString());
-    });
-  }
-
-  React.useEffect(() => {
-    setLoading(true);
-    loadData(path);
-    setLoading(false);
-
-    return (() => {
-      cancelRequests([
-        cancelMetadataSignal.current!,
-      ]);
-    })
-  }, [path]);
+    } catch (error) {
+      showDataFetchError(error);
+    } finally {
+      setIsProcessingData(false);
+    }
+  }, [path, getObjectInfoMapping]);
+
+  // Coordinate API calls - process data when both calls complete
+  useEffect(() => {
+    if (!summaryAPI.loading && !quotaAPI.loading && 
+        summaryAPI.data && quotaAPI.data &&
+        summaryAPI.lastUpdated && quotaAPI.lastUpdated) {
+      processMetadata(summaryAPI.data, quotaAPI.data);
+    }
+  }, [summaryAPI.loading, quotaAPI.loading, summaryAPI.data, quotaAPI.data, 
+      summaryAPI.lastUpdated, quotaAPI.lastUpdated, processMetadata]);
 
   return (
     <Table
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/containersTable.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/containersTable.tsx
index 424d58cf245..dba9a3a350f 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/containersTable.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/containersTable.tsx
@@ -16,10 +16,9 @@
  * limitations under the License.
  */
 
-import React, {useRef} from 'react';
+import React from 'react';
 import filesize from 'filesize';
 
-import { AxiosError } from 'axios';
 import { Popover, Table } from 'antd';
 import {
   ColumnsType,
@@ -29,7 +28,7 @@ import { CheckCircleOutlined, NodeIndexOutlined } from 
'@ant-design/icons';
 
 import {getFormattedTime} from '@/v2/utils/momentUtils';
 import {showDataFetchError} from '@/utils/common';
-import {AxiosGetHelper} from '@/utils/axiosRequestHelper';
+import {fetchData} from '@/v2/hooks/useAPIData.hook';
 import {
   Container,
   ContainerKeysResponse,
@@ -182,7 +181,6 @@ const ContainerTable: React.FC<ContainerTableProps> = ({
   searchTerm = ''
 }) => {
 
-  const cancelSignal = useRef<AbortController>();
 
   function filterSelectedColumns() {
     const columnKeys = selectedColumns.map((column) => column.value);
@@ -191,15 +189,12 @@ const ContainerTable: React.FC<ContainerTableProps> = ({
     );
   }
 
-  function loadRowData(containerID: number) {
-    const { request, controller } = AxiosGetHelper(
-      `/api/v1/containers/${containerID}/keys`,
-      cancelSignal.current
-    );
-    cancelSignal.current = controller;
-
-    request.then(response => {
-      const containerKeysResponse: ContainerKeysResponse = response.data;
+  async function loadRowData(containerID: number) {
+    try {
+      const containerKeysResponse = await fetchData<ContainerKeysResponse>(
+        `/api/v1/containers/${containerID}/keys`
+      );
+      
       expandedRowSetter({
         ...expandedRow,
         [containerID]: {
@@ -209,7 +204,7 @@ const ContainerTable: React.FC<ContainerTableProps> = ({
           totalCount: containerKeysResponse.totalCount
         }
       });
-    }).catch(error => {
+    } catch (error) {
       expandedRowSetter({
         ...expandedRow,
         [containerID]: {
@@ -217,8 +212,8 @@ const ContainerTable: React.FC<ContainerTableProps> = ({
           loading: false
         }
       });
-      showDataFetchError((error as AxiosError).toString());
-    });
+      showDataFetchError(error);
+    }
   }
 
   function getFilteredData(data: Container[]) {
@@ -236,9 +231,6 @@ const ContainerTable: React.FC<ContainerTableProps> = ({
     if (expanded) {
       loadRowData(record.containerID);
     }
-    else {
-      cancelSignal.current && cancelSignal.current.abort();
-    }
   }
 
   function expandedRowRender(record: Container) {
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx
index 565acde6db7..1548b36fbe0 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx
@@ -16,8 +16,7 @@
  * limitations under the License.
  */
 
-import React from 'react';
-import { AxiosError } from 'axios';
+import React, { useState, useEffect } from 'react';
 import {
   Dropdown,
   Menu,
@@ -38,7 +37,7 @@ import { ValueType } from 'react-select';
 import Search from '@/v2/components/search/search';
 import SingleSelect, { Option } from '@/v2/components/select/singleSelect';
 import { showDataFetchError } from '@/utils/common';
-import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
 import { useDebounce } from '@/v2/hooks/useDebounce';
 import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
 
@@ -48,7 +47,6 @@ import {
   Pipelines
 } from '@/v2/types/insights.types';
 
-
 //-----Types-----
 type ContainerMismatchTableProps = {
   paginationConfig: TablePaginationConfig;
@@ -58,6 +56,10 @@ type ContainerMismatchTableProps = {
   onRowExpand: (arg0: boolean, arg1: any) => void;
 }
 
+const DEFAULT_MISMATCH_RESPONSE: MismatchContainersResponse = {
+  containerDiscrepancyInfo: []
+};
+
 //-----Components------
 const ContainerMismatchTable: React.FC<ContainerMismatchTableProps> = ({
   paginationConfig,
@@ -66,19 +68,40 @@ const ContainerMismatchTable: 
React.FC<ContainerMismatchTableProps> = ({
   expandedRowRender,
   handleLimitChange
 }) => {
+  const [data, setData] = useState<Container[]>([]);
+  const [searchTerm, setSearchTerm] = useState<string>('');
+  const [missingIn, setMissingIn] = useState<string>('OM');
 
-  const [loading, setLoading] = React.useState<boolean>(false);
-  const [data, setData] = React.useState<Container[]>();
-  const [searchTerm, setSearchTerm] = React.useState<string>('');
-
-  const cancelSignal = React.useRef<AbortController>();
   const debouncedSearch = useDebounce(searchTerm, 300);
 
+  // Use the modern hooks pattern
+  const mismatchData = useApiData<MismatchContainersResponse>(
+    `/api/v1/containers/mismatch?limit=${limit.value}&missingIn=${missingIn}`,
+    DEFAULT_MISMATCH_RESPONSE,
+    {
+      retryAttempts: 2,
+      initialFetch: false,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+
+  // Process data when it changes
+  useEffect(() => {
+    if (mismatchData.data && mismatchData.data.containerDiscrepancyInfo) {
+      setData(mismatchData.data.containerDiscrepancyInfo);
+    }
+  }, [mismatchData.data]);
+
+  // Refetch when limit or missingIn changes
+  useEffect(() => {
+    mismatchData.refetch();
+  }, [limit.value, missingIn]);
+
   const handleExistAtChange: FilterMenuProps['onClick'] = ({ key }) => {
     if (key === 'OM') {
-      fetchMismatchContainers('SCM');
+      setMissingIn('SCM');
     } else {
-      fetchMismatchContainers('OM');
+      setMissingIn('OM');
     }
   }
 
@@ -94,7 +117,6 @@ const ContainerMismatchTable: 
React.FC<ContainerMismatchTableProps> = ({
       dataIndex: 'containerId',
       key: 'containerId',
       width: '20%'
-
     },
     {
       title: 'Count Of Keys',
@@ -150,33 +172,6 @@ const ContainerMismatchTable: 
React.FC<ContainerMismatchTableProps> = ({
     }
   ];
 
-  function fetchMismatchContainers(missingIn: string) {
-    setLoading(true);
-    const { request, controller } = AxiosGetHelper(
-      
`/api/v1/containers/mismatch?limit=${limit.value}&missingIn=${missingIn}`,
-      cancelSignal.current
-    );
-
-    cancelSignal.current = controller;
-    request.then(response => {
-      const mismatchedContainers: MismatchContainersResponse = response?.data;
-      setData(mismatchedContainers?.containerDiscrepancyInfo ?? []);
-      setLoading(false);
-    }).catch(error => {
-      setLoading(false);
-      showDataFetchError((error as AxiosError).toString());
-    })
-  }
-
-  React.useEffect(() => {
-    //Fetch containers missing in OM by default
-    fetchMismatchContainers('OM');
-
-    return (() => {
-      cancelSignal.current && cancelSignal.current.abort();
-    })
-  }, [limit.value]);
-
   return (
     <>
       <div className='table-header-section'>
@@ -203,7 +198,7 @@ const ContainerMismatchTable: 
React.FC<ContainerMismatchTableProps> = ({
         }}
         dataSource={filterData(data)}
         columns={COLUMNS}
-        loading={loading}
+        loading={mismatchData.loading}
         pagination={paginationConfig}
         rowKey='containerId'
         locale={{ filterTitle: '' }}
@@ -212,4 +207,4 @@ const ContainerMismatchTable: 
React.FC<ContainerMismatchTableProps> = ({
   )
 }
 
-export default ContainerMismatchTable;
\ No newline at end of file
+export default ContainerMismatchTable;
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx
index 190754b9388..1331221b6a5 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx
@@ -16,8 +16,7 @@
  * limitations under the License.
  */
 
-import React from 'react';
-import { AxiosError } from 'axios';
+import React, { useState, useEffect } from 'react';
 import Table, {
   ColumnsType,
   TablePaginationConfig
@@ -26,9 +25,9 @@ import { ValueType } from 'react-select';
 
 import Search from '@/v2/components/search/search';
 import SingleSelect, { Option } from '@/v2/components/select/singleSelect';
-import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
 import { byteToSize, showDataFetchError } from '@/utils/common';
 import { getFormattedTime } from '@/v2/utils/momentUtils';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
 import { useDebounce } from '@/v2/hooks/useDebounce';
 import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
 
@@ -41,6 +40,10 @@ type DeletePendingDirTableProps = {
   handleLimitChange: (arg0: ValueType<Option, false>) => void;
 }
 
+const DEFAULT_DELETE_PENDING_DIRS_RESPONSE = {
+  deletedDirInfo: []
+};
+
 //-----Constants------
 const COLUMNS: ColumnsType<DeletedDirInfo> = [{
   title: 'Directory Name',
@@ -73,44 +76,40 @@ const DeletePendingDirTable: 
React.FC<DeletePendingDirTableProps> = ({
   paginationConfig,
   handleLimitChange
 }) => {
+  const [data, setData] = useState<DeletedDirInfo[]>([]);
+  const [searchTerm, setSearchTerm] = useState<string>('');
 
-  const [loading, setLoading] = React.useState<boolean>(false);
-  const [data, setData] = React.useState<DeletedDirInfo[]>();
-  const [searchTerm, setSearchTerm] = React.useState<string>('');
-
-  const cancelSignal = React.useRef<AbortController>();
   const debouncedSearch = useDebounce(searchTerm, 300);
 
+  // Use the modern hooks pattern
+  const deletePendingDirsData = useApiData<{ deletedDirInfo: DeletedDirInfo[] 
}>(
+    `/api/v1/keys/deletePending/dirs?limit=${limit.value}`,
+    DEFAULT_DELETE_PENDING_DIRS_RESPONSE,
+    {
+      retryAttempts: 2,
+      initialFetch: false,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+
+  // Process data when it changes
+  useEffect(() => {
+    if (deletePendingDirsData.data && 
deletePendingDirsData.data.deletedDirInfo) {
+      setData(deletePendingDirsData.data.deletedDirInfo);
+    }
+  }, [deletePendingDirsData.data]);
+
+  // Refetch when limit changes
+  useEffect(() => {
+    deletePendingDirsData.refetch();
+  }, [limit.value]);
+
   function filterData(data: DeletedDirInfo[] | undefined) {
     return data?.filter(
       (data: DeletedDirInfo) => data.key.includes(debouncedSearch)
     );
   }
 
-  function loadData() {
-    setLoading(true);
-
-    const { request, controller } = AxiosGetHelper(
-      `/api/v1/keys/deletePending/dirs?limit=${limit.value}`,
-      cancelSignal.current
-    );
-    cancelSignal.current = controller;
-
-    request.then(response => {
-      setData(response?.data?.deletedDirInfo ?? []);
-      setLoading(false);
-    }).catch(error => {
-      setLoading(false);
-      showDataFetchError((error as AxiosError).toString());
-    });
-  }
-
-  React.useEffect(() => {
-    loadData();
-
-    return (() => cancelSignal.current && cancelSignal.current.abort());
-  }, [limit.value]);
-
   return (<>
     <div className='table-header-section'>
       <div className='table-filter-section'>
@@ -129,7 +128,7 @@ const DeletePendingDirTable: 
React.FC<DeletePendingDirTableProps> = ({
         onChange={() => { }} />
     </div>
     <Table
-      loading={loading}
+      loading={deletePendingDirsData.loading}
       dataSource={filterData(data)}
       columns={COLUMNS}
       pagination={paginationConfig}
@@ -139,4 +138,4 @@ const DeletePendingDirTable: 
React.FC<DeletePendingDirTableProps> = ({
   </>)
 }
 
-export default DeletePendingDirTable;
\ No newline at end of file
+export default DeletePendingDirTable;
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx
index 81ed9020c2b..bf32bd155de 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx
@@ -16,8 +16,7 @@
 * limitations under the License.
 */
 
-import React from 'react';
-import { AxiosError } from 'axios';
+import React, { useState, useEffect } from 'react';
 import Table, {
   ColumnsType,
   TablePaginationConfig
@@ -27,8 +26,8 @@ import { ValueType } from 'react-select';
 import Search from '@/v2/components/search/search';
 import SingleSelect, { Option } from '@/v2/components/select/singleSelect';
 import ExpandedPendingKeysTable from 
'@/v2/components/tables/insights/expandedPendingKeysTable';
-import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
 import { byteToSize, showDataFetchError } from '@/utils/common';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
 import { useDebounce } from '@/v2/hooks/useDebounce';
 import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
 
@@ -55,6 +54,10 @@ type ExpandedDeletePendingKeys = {
   omKeyInfoList: DeletePendingKey[]
 }
 
+const DEFAULT_DELETE_PENDING_KEYS_RESPONSE: DeletePendingKeysResponse = {
+  deletedKeyInfo: []
+};
+
 //------Constants------
 const COLUMNS: ColumnsType<DeletePendingKeysColumns> = [
   {
@@ -80,52 +83,39 @@ const COLUMNS: ColumnsType<DeletePendingKeysColumns> = [
   }
 ];
 
-let expandedDeletePendingKeys: ExpandedDeletePendingKeys[] = [];
-
 //-----Components------
 const DeletePendingKeysTable: React.FC<DeletePendingKeysTableProps> = ({
   paginationConfig,
   limit,
   handleLimitChange
 }) => {
-  const [loading, setLoading] = React.useState<boolean>(false);
-  const [data, setData] = React.useState<DeletePendingKeysColumns[]>();
-  const [searchTerm, setSearchTerm] = React.useState<string>('');
+  const [data, setData] = useState<DeletePendingKeysColumns[]>([]);
+  const [searchTerm, setSearchTerm] = useState<string>('');
+  const [expandedDeletePendingKeys, setExpandedDeletePendingKeys] = 
useState<ExpandedDeletePendingKeys[]>([]);
 
-  const cancelSignal = React.useRef<AbortController>();
   const debouncedSearch = useDebounce(searchTerm, 300);
 
-  function filterData(data: DeletePendingKeysColumns[] | undefined) {
-    return data?.filter(
-      (data: DeletePendingKeysColumns) => 
data.keyName.includes(debouncedSearch)
-    );
-  }
-
-  function expandedRowRender(record: DeletePendingKeysColumns) {
-    const filteredData = expandedDeletePendingKeys?.flatMap((info) => (
-      info.omKeyInfoList?.filter((key) => key.keyName === record.keyName)
-    ));
-    return (
-      <ExpandedPendingKeysTable
-        data={filteredData}
-        paginationConfig={paginationConfig} />
-    )
-  }
-
-  function fetchDeletePendingKeys() {
-    setLoading(true);
-    const { request, controller } = AxiosGetHelper(
-      `/api/v1/keys/deletePending?limit=${limit.value}`,
-      cancelSignal.current
-    );
-    cancelSignal.current = controller;
+  // Use the modern hooks pattern
+  const deletePendingKeysData = useApiData<DeletePendingKeysResponse>(
+    `/api/v1/keys/deletePending?limit=${limit.value}`,
+    DEFAULT_DELETE_PENDING_KEYS_RESPONSE,
+    {
+      retryAttempts: 2,
+      initialFetch: false,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+
+  // Process data when it changes
+  useEffect(() => {
+    if (deletePendingKeysData.data && 
deletePendingKeysData.data.deletedKeyInfo) {
+      const deletePendingKeys = deletePendingKeysData.data;
+      let deletedKeyData: DeletePendingKeysColumns[] = [];
+      let expandedData: ExpandedDeletePendingKeys[] = [];
 
-    request.then(response => {
-      const deletePendingKeys: DeletePendingKeysResponse = response?.data;
-      let deletedKeyData = [];
       // Sum up the data size and organize related key information
-      deletedKeyData = deletePendingKeys?.deletedKeyInfo?.flatMap((keyInfo) => 
{
-        expandedDeletePendingKeys.push(keyInfo);
+      deletedKeyData = deletePendingKeys.deletedKeyInfo?.flatMap((keyInfo) => {
+        expandedData.push(keyInfo);
         let count = 0;
         let item: DeletePendingKey = keyInfo.omKeyInfoList?.reduce((obj, curr) 
=> {
           count += 1;
@@ -139,24 +129,35 @@ const DeletePendingKeysTable: 
React.FC<DeletePendingKeysTableProps> = ({
           path: item.path,
           keyCount: count
         }
-      });
-      setData(deletedKeyData);
-      setLoading(false);
-    }).catch(error => {
-      setLoading(false);
-      showDataFetchError((error as AxiosError).toString());
-    })
-  }
+      }) || [];
 
-  React.useEffect(() => {
-    fetchDeletePendingKeys();
-    expandedDeletePendingKeys = [];
+      setData(deletedKeyData);
+      setExpandedDeletePendingKeys(expandedData);
+    }
+  }, [deletePendingKeysData.data]);
 
-    return (() => {
-      cancelSignal.current && cancelSignal.current.abort();
-    })
+  // Refetch when limit changes
+  useEffect(() => {
+    deletePendingKeysData.refetch();
   }, [limit.value]);
 
+  function filterData(data: DeletePendingKeysColumns[] | undefined) {
+    return data?.filter(
+      (data: DeletePendingKeysColumns) => 
data.keyName.includes(debouncedSearch)
+    );
+  }
+
+  function expandedRowRender(record: DeletePendingKeysColumns) {
+    const filteredData = expandedDeletePendingKeys?.flatMap((info) => (
+      info.omKeyInfoList?.filter((key) => key.keyName === record.keyName)
+    ));
+    return (
+      <ExpandedPendingKeysTable
+        data={filteredData}
+        paginationConfig={paginationConfig} />
+    )
+  }
+
   return (
     <>
       <div className='table-header-section'>
@@ -182,7 +183,7 @@ const DeletePendingKeysTable: 
React.FC<DeletePendingKeysTableProps> = ({
         }}
         dataSource={filterData(data)}
         columns={COLUMNS}
-        loading={loading}
+        loading={deletePendingKeysData.loading}
         pagination={paginationConfig}
         rowKey='keyName'
         locale={{ filterTitle: '' }}
@@ -191,4 +192,4 @@ const DeletePendingKeysTable: 
React.FC<DeletePendingKeysTableProps> = ({
   )
 }
 
-export default DeletePendingKeysTable;
\ No newline at end of file
+export default DeletePendingKeysTable;
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx
index 9f665857b88..4139bf97a40 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx
@@ -16,8 +16,7 @@
  * limitations under the License.
  */
 
-import React from 'react';
-import { AxiosError } from 'axios';
+import React, { useState, useEffect } from 'react';
 import Table, {
   ColumnsType,
   TablePaginationConfig
@@ -26,8 +25,8 @@ import { ValueType } from 'react-select';
 
 import Search from '@/v2/components/search/search';
 import SingleSelect, { Option } from '@/v2/components/select/singleSelect';
-import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
 import { showDataFetchError } from '@/utils/common';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
 import { useDebounce } from '@/v2/hooks/useDebounce';
 import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
 
@@ -46,6 +45,10 @@ type DeletedContainerKeysTableProps = {
   expandedRowRender: (arg0: any) => JSX.Element;
 }
 
+const DEFAULT_DELETED_CONTAINER_KEYS_RESPONSE: DeletedContainerKeysResponse = {
+  containers: []
+};
+
 //------Constants------
 const COLUMNS: ColumnsType<Container> = [
   {
@@ -84,47 +87,40 @@ const DeletedContainerKeysTable: 
React.FC<DeletedContainerKeysTableProps> = ({
   onRowExpand,
   expandedRowRender
 }) => {
+  const [data, setData] = useState<Container[]>([]);
+  const [searchTerm, setSearchTerm] = useState<string>('');
 
-  const [loading, setLoading] = React.useState<boolean>(false);
-  const [data, setData] = React.useState<Container[]>();
-  const [searchTerm, setSearchTerm] = React.useState<string>('');
-
-  const cancelSignal = React.useRef<AbortController>();
   const debouncedSearch = useDebounce(searchTerm, 300);
 
+  // Use the modern hooks pattern
+  const deletedContainerKeysData = useApiData<DeletedContainerKeysResponse>(
+    `/api/v1/containers/mismatch/deleted?limit=${limit.value}`,
+    DEFAULT_DELETED_CONTAINER_KEYS_RESPONSE,
+    {
+      retryAttempts: 2,
+      initialFetch: false,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+
+  // Process data when it changes
+  useEffect(() => {
+    if (deletedContainerKeysData.data && 
deletedContainerKeysData.data.containers) {
+      setData(deletedContainerKeysData.data.containers);
+    }
+  }, [deletedContainerKeysData.data]);
+
+  // Refetch when limit changes
+  useEffect(() => {
+    deletedContainerKeysData.refetch();
+  }, [limit.value]);
+
   function filterData(data: Container[] | undefined) {
     return data?.filter(
       (data: Container) => 
data.containerId.toString().includes(debouncedSearch)
     );
   }
 
-  function fetchDeletedKeys() {
-    const { request, controller } = AxiosGetHelper(
-      `/api/v1/containers/mismatch/deleted?limit=${limit.value}`,
-      cancelSignal.current
-    )
-    cancelSignal.current = controller;
-
-    request.then(response => {
-      setLoading(true);
-      const deletedContainerKeys: DeletedContainerKeysResponse = 
response?.data;
-      setData(deletedContainerKeys?.containers ?? []);
-      setLoading(false);
-    }).catch(error => {
-      setLoading(false);
-      showDataFetchError((error as AxiosError).toString());
-    });
-  }
-
-  React.useEffect(() => {
-    fetchDeletedKeys();
-
-    return (() => {
-      cancelSignal.current && cancelSignal.current.abort();
-    })
-  }, [limit.value]);
-
-
   return (
     <>
       <div className='table-header-section'>
@@ -151,7 +147,7 @@ const DeletedContainerKeysTable: 
React.FC<DeletedContainerKeysTableProps> = ({
         }}
         dataSource={filterData(data)}
         columns={COLUMNS}
-        loading={loading}
+        loading={deletedContainerKeysData.loading}
         pagination={paginationConfig}
         rowKey='containerId'
         locale={{ filterTitle: '' }}
@@ -160,4 +156,4 @@ const DeletedContainerKeysTable: 
React.FC<DeletedContainerKeysTableProps> = ({
   )
 }
 
-export default DeletedContainerKeysTable;
\ No newline at end of file
+export default DeletedContainerKeysTable;
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx
index 38c57f4cef2..9ee92cd5e4e 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx
@@ -17,7 +17,6 @@
 */
 
 import React from 'react';
-import { AxiosError } from 'axios';
 import {
   Dropdown,
   Menu,
@@ -33,10 +32,10 @@ import { ValueType } from 'react-select';
 
 import Search from '@/v2/components/search/search';
 import SingleSelect, { Option } from '@/v2/components/select/singleSelect';
-import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
 import { byteToSize, showDataFetchError } from '@/utils/common';
 import { getFormattedTime } from '@/v2/utils/momentUtils';
 import { useDebounce } from '@/v2/hooks/useDebounce';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
 import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
 
 import { OpenKeys, OpenKeysResponse } from '@/v2/types/insights.types';
@@ -55,57 +54,53 @@ const OpenKeysTable: React.FC<OpenKeysTableProps> = ({
   paginationConfig,
   handleLimitChange
 }) => {
-  const [loading, setLoading] = React.useState<boolean>(false);
-  const [data, setData] = React.useState<OpenKeys[]>();
+  const [isFso, setIsFso] = React.useState<boolean>(true);
   const [searchTerm, setSearchTerm] = React.useState<string>('');
-
-  const cancelSignal = React.useRef<AbortController>();
   const debouncedSearch = useDebounce(searchTerm, 300);
 
+  const { 
+    data: openKeysResponse, 
+    loading, 
+  } = useApiData<OpenKeysResponse>(
+    
`/api/v1/keys/open?includeFso=${isFso}&includeNonFso=${!isFso}&limit=${limit.value}`,
+    { 
+      lastKey: '',
+      replicatedDataSize: 0,
+      unreplicatedDataSize: 0,
+      fso: [], 
+      nonFSO: [] 
+    },
+    {
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+
+  // Transform the data based on FSO selection
+  const data = React.useMemo(() => {
+    let allOpenKeys: OpenKeys[];
+    if (isFso) {
+      allOpenKeys = openKeysResponse['fso']?.map((key: OpenKeys) => ({
+        ...key,
+        type: 'FSO'
+      })) ?? [];
+    } else {
+      allOpenKeys = openKeysResponse['nonFSO']?.map((key: OpenKeys) => ({
+        ...key,
+        type: 'Non FSO'
+      })) ?? [];
+    }
+    return allOpenKeys;
+  }, [openKeysResponse, isFso]);
+
   function filterData(data: OpenKeys[] | undefined) {
     return data?.filter(
       (data: OpenKeys) => data.path.includes(debouncedSearch)
     );
   }
 
-  function fetchOpenKeys(isFso: boolean) {
-    setLoading(true);
-
-    const { request, controller } = AxiosGetHelper(
-      
`/api/v1/keys/open?includeFso=${isFso}&includeNonFso=${!isFso}&limit=${limit.value}`,
-      cancelSignal.current
-    );
-    cancelSignal.current = controller;
-
-    request.then(response => {
-      const openKeys: OpenKeysResponse = response?.data ?? { 'fso': [] };
-      let allOpenKeys: OpenKeys[];
-      if (isFso) {
-        allOpenKeys = openKeys['fso']?.map((key: OpenKeys) => ({
-          ...key,
-          type: 'FSO'
-        })) ?? [];
-      } else {
-        allOpenKeys = openKeys['nonFSO']?.map((key: OpenKeys) => ({
-          ...key,
-          type: 'Non FSO'
-        })) ?? [];
-      }
-
-      setData(allOpenKeys);
-      setLoading(false);
-    }).catch(error => {
-      setLoading(false);
-      showDataFetchError((error as AxiosError).toString());
-    });
-  }
-
   const handleKeyTypeChange: MenuProps['onClick'] = (e) => {
-    if (e.key === 'fso') {
-      fetchOpenKeys(true);
-    } else {
-      fetchOpenKeys(false);
-    }
+    // The hook will automatically refetch when the URL changes due to isFso 
change
+    setIsFso(e.key === 'fso');
   }
 
   const COLUMNS: ColumnsType<OpenKeys> = [{
@@ -173,13 +168,6 @@ const OpenKeysTable: React.FC<OpenKeysTableProps> = ({
     render: (type: string) => <div key={type}>{type}</div>
   }];
 
-  React.useEffect(() => {
-    // Fetch FSO open keys by default
-    fetchOpenKeys(true);
-
-    return (() => cancelSignal.current && cancelSignal.current.abort());
-  }, [limit.value]);
-
   return (
     <>
       <div className='table-header-section'>
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useAPIData.hook.ts
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useAPIData.hook.ts
index dfcdec0cefa..cc97a599a0f 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useAPIData.hook.ts
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useAPIData.hook.ts
@@ -17,105 +17,120 @@
  */
 
 import { useState, useEffect, useRef } from 'react';
-import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
+import axios, { AxiosError, AxiosRequestConfig } from 'axios';
+
+export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
 
 export interface ApiState<T> {
   data: T;
   loading: boolean;
   error: string | null;
   lastUpdated: number | null;
+  success: boolean;
 }
 
 export interface UseApiDataOptions {
+  method?: HttpMethod;
   retryAttempts?: number;
   retryDelay?: number;
   initialFetch?: boolean;
-  onError?: (error: string) => void;
-};
+  onError?: (error: AxiosError | string | unknown) => void;
+  onSuccess?: (data: any) => void;
+}
 
 export function useApiData<T>(
   url: string,
   defaultValue: T,
   options: UseApiDataOptions = {}
 ): ApiState<T> & {
-  refetch: () => void;
+  execute: (data?: any) => Promise<any>;
+  refetch: () => Promise<any>;
   clearError: () => void;
+  reset: () => void;
 } {
   const {
+    method = 'GET',
     retryAttempts = 3,
     retryDelay = 1000,
-    initialFetch = true,
-    onError
+    initialFetch = method === 'GET',
+    onError,
+    onSuccess
   } = options;
 
   const [state, setState] = useState<ApiState<T>>({
     data: defaultValue,
     loading: initialFetch,
     error: null,
-    lastUpdated: null
+    lastUpdated: null,
+    success: false
   });
 
   const controllerRef = useRef<AbortController>();
   const retryCountRef = useRef(0);
   const retryTimeoutRef = useRef<NodeJS.Timeout>();
-  
-  // Store stable references
-  const urlRef = useRef(url);
-  const retryAttemptsRef = useRef(retryAttempts);
-  const retryDelayRef = useRef(retryDelay);
-  const onErrorRef = useRef(onError);
-
-  // Update refs when props change
-  useEffect(() => {
-    urlRef.current = url;
-  }, [url]);
-
-  useEffect(() => {
-    retryAttemptsRef.current = retryAttempts;
-  }, [retryAttempts]);
-
-  useEffect(() => {
-    retryDelayRef.current = retryDelay;
-  }, [retryDelay]);
-
-  useEffect(() => {
-    onErrorRef.current = onError;
-  }, [onError]);
+  const mountedRef = useRef(false);
 
+  const executeRequest = async (requestData?: any, isRetry = false) => {
+    // Don't make requests if URL is empty or falsy
+    if (!url || url.trim() === '') {
+      return Promise.reject(new Error('URL is required'));
+    }
 
-  const fetchData = async (isRetry = false) => {
     if (!isRetry) {
-      setState(prev => ({ ...prev, loading: true, error: null }));
+      setState(prev => ({ ...prev, loading: true, error: null, success: false 
}));
       retryCountRef.current = 0;
     }
 
+    // Cancel previous request
+    if (controllerRef.current) {
+      controllerRef.current.abort('New request initiated');
+    }
+
+    // Create new AbortController
+    controllerRef.current = new AbortController();
+
     try {
-      const { request, controller } = AxiosGetHelper(
-        urlRef.current,
-        controllerRef.current,
-        'Request cancelled due to component unmount or new request'
-      );
-      controllerRef.current = controller;
-
-      const response = await request;
+      const config: AxiosRequestConfig = {
+        url,
+        method,
+        signal: controllerRef.current.signal,
+      };
+
+      // Add data for non-GET requests
+      if (method !== 'GET' && requestData !== undefined) {
+        config.data = requestData;
+      }
+
+      // Add query parameters for GET requests if data is provided as params
+      if (method === 'GET' && requestData !== undefined) {
+        config.params = requestData;
+      }
+
+      const response = await axios(config);
       
       setState({
         data: response.data,
         loading: false,
         error: null,
-        lastUpdated: Date.now()
+        lastUpdated: Date.now(),
+        success: true
       });
 
+      if (onSuccess) {
+        onSuccess(response.data);
+      }
+
       retryCountRef.current = 0;
+      return response;
     } catch (error: any) {
-      if (error.name === 'CanceledError') {
-        return;
+      if (error.name === 'CanceledError' || error.name === 'AbortError') {
+        return Promise.reject(error);
       }
 
       const errorMessage = error.response?.data?.message ||
                           error.response?.statusText ||
                           error.message ||
-                          `Request failed with status: 
${error.response?.status || 'unknown'}`;
+                          `${method} request failed with status: 
${error.response?.status || 'unknown'}`;
 
       // Clear any existing retry timeout
       if (retryTimeoutRef.current) {
@@ -123,65 +138,122 @@ export function useApiData<T>(
       }
 
       // Retry logic for network errors and 5xx errors
-      if (retryCountRef.current < retryAttemptsRef.current && 
+      if (retryCountRef.current < retryAttempts && 
           (!error.response?.status || error.response?.status >= 500)) {
         retryCountRef.current++;
         retryTimeoutRef.current = setTimeout(() => {
-          fetchData(true);
-        }, retryDelayRef.current * retryCountRef.current);
-        return;
+          executeRequest(requestData, true);
+        }, retryDelay * retryCountRef.current);
+        return Promise.reject(error);
       }
 
-      if (onErrorRef.current) {
-        onErrorRef.current(errorMessage);
+      if (onError) {
+        onError(error);
       }
 
       setState({
         data: defaultValue,
         loading: false,
         error: errorMessage,
-        lastUpdated: Date.now()
+        lastUpdated: Date.now(),
+        success: false
       });
+
+      return Promise.reject(error);
     }
   };
 
+  const execute = (data?: any) => {
+    return executeRequest(data);
+  };
+
   const refetch = () => {
-    fetchData();
+    return executeRequest();
   };
 
   const clearError = () => {
     setState(prev => ({ ...prev, error: null }));
   };
 
-  // Initial fetch only
+  const reset = () => {
+    setState({
+      data: defaultValue,
+      loading: false,
+      error: null,
+      lastUpdated: null,
+      success: false
+    });
+  };
+
+  // Handle initial fetch, URL changes, and cleanup
   useEffect(() => {
-    if (initialFetch) {
-      fetchData();
+    // Don't make requests if URL is empty or falsy
+    if (!url || url.trim() === '') {
+      return;
     }
 
-    // Cleanup retry timeout on unmount
-    return () => {
-      if (retryTimeoutRef.current) {
-        clearTimeout(retryTimeoutRef.current);
+    if (!mountedRef.current) {
+      // Initial mount - this is required since we might have a situation where
+      // the component is mounted but initial fetch is not enabled, hence we 
need to separate out
+      // by checking if the component is mounted or just the URL has changed.
+      mountedRef.current = true;
+      if (initialFetch && method === 'GET') {
+        executeRequest();
       }
-    };
-  }, []); // Empty dependency array
+    } else {
+      // URL changed - refetch for GET requests
+      if (method === 'GET') {
+        executeRequest();
+      }
+    }
 
-  // Cleanup on unmount
-  useEffect(() => {
+    // Cleanup on unmount
     return () => {
       if (controllerRef.current) {
-        controllerRef.current.abort('Component unmounted');
+        controllerRef.current.abort();
       }
       if (retryTimeoutRef.current) {
         clearTimeout(retryTimeoutRef.current);
       }
     };
-  }, []);
+  }, [url]); // eslint-disable-line react-hooks/exhaustive-deps
 
   return {
     ...state,
+    execute,
     refetch,
-    clearError
+    clearError,
+    reset
+  };
+}
+
+// Utility function for manual single requests (for dynamic/on-demand usage)
+export async function fetchData<T>(
+  url: string, 
+  method: HttpMethod = 'GET', 
+  data?: any
+): Promise<T> {
+  // Don't make requests if URL is empty or falsy
+  if (!url || url.trim() === '') {
+    return Promise.reject(new Error('URL is required'));
+  }
+
+  const controller = new AbortController();
+  
+  const config: AxiosRequestConfig = {
+    url,
+    method,
+    signal: controller.signal,
   };
+
+  if (method !== 'GET' && data !== undefined) {
+    config.data = data;
+  }
+
+  if (method === 'GET' && data !== undefined) {
+    config.params = data;
+  }
+
+  const response = await axios(config);
+  return response.data;
 }
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx
index 7d2c77de3c3..3d7fda9cb3f 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useEffect, useState, useCallback } from 'react';
 import moment from 'moment';
 import { ValueType } from 'react-select';
 import { useLocation } from 'react-router-dom';
@@ -28,11 +28,11 @@ import MultiSelect from 
'@/v2/components/select/multiSelect';
 import SingleSelect, { Option } from '@/v2/components/select/singleSelect';
 import BucketsTable, { COLUMNS } from '@/v2/components/tables/bucketsTable';
 
-import { AutoReloadHelper } from '@/utils/autoReloadHelper';
-import { AxiosGetHelper, cancelRequests } from "@/utils/axiosRequestHelper";
 import { showDataFetchError } from '@/utils/common';
 import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
 import { useDebounce } from '@/v2/hooks/useDebounce';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
+import { useAutoReload } from '@/v2/hooks/useAutoReload.hook';
 
 import {
   Bucket,
@@ -55,6 +55,11 @@ const defaultColumns = COLUMNS.map(column => ({
   value: column.key as string
 }));
 
+const DEFAULT_BUCKET_RESPONSE: BucketResponse = {
+  totalCount: 0,
+  buckets: []
+};
+
 function getVolumeBucketMap(data: Bucket[]) {
   const volumeBucketMap = data.reduce((
     map: Map<string, Set<Bucket>>,
@@ -91,9 +96,6 @@ function getFilteredBuckets(
 }
 
 const Buckets: React.FC<{}> = () => {
-
-  const cancelSignal = useRef<AbortController>();
-
   const [state, setState] = useState<BucketsState>({
     totalCount: 0,
     lastUpdated: 0,
@@ -102,7 +104,6 @@ const Buckets: React.FC<{}> = () => {
     bucketsUnderVolume: [],
     volumeOptions: [],
   });
-  const [loading, setLoading] = useState<boolean>(false);
   const [selectedColumns, setSelectedColumns] = 
useState<Option[]>(defaultColumns);
   const [selectedVolumes, setSelectedVolumes] = useState<Option[]>([]);
   const [selectedLimit, setSelectedLimit] = useState<Option>(LIMIT_OPTIONS[0]);
@@ -114,9 +115,20 @@ const Buckets: React.FC<{}> = () => {
   const debouncedSearch = useDebounce(searchTerm, 300);
   const { search } = useLocation();
 
+  // Use the modern hooks pattern
+  const bucketsData = useApiData<BucketResponse>(
+    `/api/v1/buckets?limit=${selectedLimit.value}`,
+    DEFAULT_BUCKET_RESPONSE,
+    {
+      retryAttempts: 2,
+      initialFetch: false,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+
   function getVolumeSearchParam() {
     return new URLSearchParams(search).get('volume');
-  };
+  }
 
   function handleVolumeChange(selected: ValueType<Option, true>) {
     const { volumeBucketMap } = state;
@@ -132,7 +144,7 @@ const Buckets: React.FC<{}> = () => {
       ...state,
       bucketsUnderVolume: selectedBuckets
     });
-  };
+  }
 
   function handleAclLinkClick(bucket: Bucket) {
     setCurrentRow(bucket);
@@ -147,41 +159,28 @@ const Buckets: React.FC<{}> = () => {
     setSelectedLimit(selected as Option);
   }
 
-  const loadData = () => {
-    setLoading(true);
-    const { request, controller } = AxiosGetHelper(
-      '/api/v1/buckets',
-      cancelSignal.current,
-      '',
-      { limit: selectedLimit.value }
-    );
-    cancelSignal.current = controller;
-    request.then(response => {
-      const bucketsResponse: BucketResponse = response.data;
-      const totalCount = bucketsResponse.totalCount;
-      const buckets: Bucket[] = bucketsResponse.buckets;
-
-      const dataSource: Bucket[] = buckets?.map(bucket => {
-        return {
-          volumeName: bucket.volumeName,
-          name: bucket.name,
-          versioning: bucket.versioning,
-          storageType: bucket.storageType,
-          bucketLayout: bucket.bucketLayout,
-          creationTime: bucket.creationTime,
-          modificationTime: bucket.modificationTime,
-          sourceVolume: bucket.sourceVolume,
-          sourceBucket: bucket.sourceBucket,
-          usedBytes: bucket.usedBytes,
-          usedNamespace: bucket.usedNamespace,
-          quotaInBytes: bucket.quotaInBytes,
-          quotaInNamespace: bucket.quotaInNamespace,
-          owner: bucket.owner,
-          acls: bucket.acls
-        };
-      }) ?? [];
-
-      const volumeBucketMap: Map<string, Set<Bucket>> = 
getVolumeBucketMap(dataSource);
+  // Process buckets data when it changes
+  useEffect(() => {
+    if (bucketsData.data && bucketsData.data.buckets) {
+      const buckets: Bucket[] = bucketsData.data.buckets.map(bucket => ({
+        volumeName: bucket.volumeName,
+        name: bucket.name,
+        versioning: bucket.versioning,
+        storageType: bucket.storageType,
+        bucketLayout: bucket.bucketLayout,
+        creationTime: bucket.creationTime,
+        modificationTime: bucket.modificationTime,
+        sourceVolume: bucket.sourceVolume,
+        sourceBucket: bucket.sourceBucket,
+        usedBytes: bucket.usedBytes,
+        usedNamespace: bucket.usedNamespace,
+        quotaInBytes: bucket.quotaInBytes,
+        quotaInNamespace: bucket.quotaInNamespace,
+        owner: bucket.owner,
+        acls: bucket.acls
+      }));
+
+      const volumeBucketMap: Map<string, Set<Bucket>> = 
getVolumeBucketMap(buckets);
 
       // Set options for volume selection dropdown
       const volumeOptions: Option[] = Array.from(
@@ -191,30 +190,34 @@ const Buckets: React.FC<{}> = () => {
         value: k
       }));
 
-      setLoading(false);
+      setState(prevState => ({
+        ...prevState,
+        totalCount: bucketsData.data.totalCount,
+        volumeBucketMap: volumeBucketMap,
+        volumeOptions: volumeOptions,
+        lastUpdated: Number(moment())
+      }));
 
+      // Set default volumes if none selected
       setSelectedVolumes((prevState) => {
         if (prevState.length === 0) return volumeOptions;
         return prevState;
       });
+    }
+  }, [bucketsData.data]);
 
-      setState({
-        ...state,
-        totalCount: totalCount,
-        volumeBucketMap: volumeBucketMap,
-        volumeOptions: volumeOptions,
-        lastUpdated: Number(moment())
-      });
-    }).catch(error => {
-      setLoading(false);
-      showDataFetchError(error.toString());
-    });
-  }
-
-  const autoReloadHelper: AutoReloadHelper = new AutoReloadHelper(loadData);
+  // Update buckets under volume when volume selection or data changes
+  useEffect(() => {
+    setState(prevState => ({
+      ...prevState,
+      bucketsUnderVolume: getFilteredBuckets(
+        selectedVolumes,
+        prevState.volumeBucketMap
+      )
+    }));
+  }, [selectedVolumes, state.volumeBucketMap]);
 
   useEffect(() => {
-    autoReloadHelper.startPolling();
     const initialVolume = getVolumeSearchParam();
     if (initialVolume) {
       setSelectedVolumes([{
@@ -222,30 +225,14 @@ const Buckets: React.FC<{}> = () => {
         value: initialVolume
       }]);
     }
-    loadData();
-
-    return (() => {
-      autoReloadHelper.stopPolling();
-      cancelRequests([cancelSignal.current!]);
-    })
   }, []);
 
-  useEffect(() => {
-    // If the data is fetched, we need to regenerate the columns
-    // To make sure the filters are properly applied
-    setState({
-      ...state,
-      bucketsUnderVolume: getFilteredBuckets(
-        selectedVolumes,
-        state.volumeBucketMap
-      )
-    });
-  }, [state.volumeBucketMap])
+  // Create refresh function for auto-reload
+  const loadBucketsData = () =>{
+    bucketsData.refetch();
+  };
 
-  // If limit changes, load new data
-  useEffect(() => {
-    loadData();
-  }, [selectedLimit.value]);
+  const autoReload = useAutoReload(loadBucketsData);
 
   const {
     lastUpdated, columnOptions,
@@ -257,10 +244,10 @@ const Buckets: React.FC<{}> = () => {
       <div className='page-header-v2'>
         Buckets
         <AutoReloadPanel
-          isLoading={loading}
+          isLoading={bucketsData.loading}
           lastRefreshed={lastUpdated}
-          togglePolling={autoReloadHelper.handleAutoReloadToggle}
-          onReload={loadData}
+          togglePolling={autoReload.handleAutoReloadToggle}
+          onReload={loadBucketsData}
         />
       </div>
       <div className='data-container'>
@@ -305,7 +292,7 @@ const Buckets: React.FC<{}> = () => {
               }} />
           </div>
           <BucketsTable
-            loading={loading}
+            loading={bucketsData.loading}
             data={bucketsUnderVolume}
             handleAclClick={handleAclLinkClick}
             selectedColumns={selectedColumns}
@@ -323,4 +310,4 @@ const Buckets: React.FC<{}> = () => {
   )
 }
 
-export default Buckets;
\ No newline at end of file
+export default Buckets;
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/containers/containers.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/containers/containers.tsx
index 3a784bb9932..6bd9f6fc725 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/containers/containers.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/containers/containers.tsx
@@ -16,9 +16,8 @@
  * limitations under the License.
  */
 
-import React, { useRef, useState } from "react";
+import React, { useState, useCallback } from "react";
 import moment from "moment";
-import { AxiosError } from "axios";
 import { Card, Row, Tabs } from "antd";
 import { ValueType } from "react-select/src/types";
 
@@ -27,9 +26,9 @@ import MultiSelect, { Option } from 
"@/v2/components/select/multiSelect";
 import ContainerTable, { COLUMNS } from 
"@/v2/components/tables/containersTable";
 import AutoReloadPanel from "@/components/autoReloadPanel/autoReloadPanel";
 import { showDataFetchError } from "@/utils/common";
-import { AutoReloadHelper } from "@/utils/autoReloadHelper";
-import { AxiosGetHelper, cancelRequests } from "@/utils/axiosRequestHelper";
 import { useDebounce } from "@/v2/hooks/useDebounce";
+import { useApiData } from "@/v2/hooks/useAPIData.hook";
+import { useAutoReload } from "@/v2/hooks/useAutoReload.hook";
 
 import {
   Container,
@@ -39,7 +38,6 @@ import {
 
 import './containers.less';
 
-
 const SearchableColumnOpts = [{
   label: 'Container ID',
   value: 'containerID'
@@ -53,10 +51,11 @@ const defaultColumns = COLUMNS.map(column => ({
   value: column.key as string
 }));
 
-const Containers: React.FC<{}> = () => {
-
-  const cancelSignal = useRef<AbortController>();
+const DEFAULT_CONTAINERS_RESPONSE = {
+  containers: []
+};
 
+const Containers: React.FC<{}> = () => {
   const [state, setState] = useState<ContainerState>({
     lastUpdated: 0,
     columnOptions: defaultColumns,
@@ -67,8 +66,6 @@ const Containers: React.FC<{}> = () => {
     mismatchedReplicaContainerData: []
   });
   const [expandedRow, setExpandedRow] = useState<ExpandedRow>({});
-
-  const [loading, setLoading] = useState<boolean>(false);
   const [selectedColumns, setSelectedColumns] = 
useState<Option[]>(defaultColumns);
   const [searchTerm, setSearchTerm] = useState<string>('');
   const [selectedTab, setSelectedTab] = useState<string>('1');
@@ -76,18 +73,21 @@ const Containers: React.FC<{}> = () => {
 
   const debouncedSearch = useDebounce(searchTerm, 300);
 
-  function loadData() {
-    setLoading(true);
-
-    const { request, controller } = AxiosGetHelper(
-      '/api/v1/containers/unhealthy',
-      cancelSignal.current
-    );
-
-    cancelSignal.current = controller;
+  // Use the modern hooks pattern
+  const containersData = useApiData<{ containers: Container[] }>(
+    '/api/v1/containers/unhealthy',
+    DEFAULT_CONTAINERS_RESPONSE,
+    {
+      retryAttempts: 2,
+      initialFetch: false,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
 
-    request.then(response => {
-      const containers: Container[] = response.data.containers;
+  // Process containers data when it changes
+  React.useEffect(() => {
+    if (containersData.data && containersData.data.containers) {
+      const containers: Container[] = containersData.data.containers;
 
       const missingContainerData: Container[] = containers?.filter(
         container => container.containerState === 'MISSING'
@@ -102,45 +102,50 @@ const Containers: React.FC<{}> = () => {
         container => container.containerState === 'MIS_REPLICATED'
       ) ?? [];
       const mismatchedReplicaContainerData: Container[] = containers?.filter(
-        container => container.containerState === 'REPLICA_MISMATCH'
+        container => container.containerState === 'MISMATCHED_REPLICA'
       ) ?? [];
 
       setState({
         ...state,
-        missingContainerData: missingContainerData,
-        underReplicatedContainerData: underReplicatedContainerData,
-        overReplicatedContainerData: overReplicatedContainerData,
-        misReplicatedContainerData: misReplicatedContainerData,
-        mismatchedReplicaContainerData: mismatchedReplicaContainerData,
+        missingContainerData,
+        underReplicatedContainerData,
+        overReplicatedContainerData,
+        misReplicatedContainerData,
+        mismatchedReplicaContainerData,
         lastUpdated: Number(moment())
       });
-      setLoading(false)
-    }).catch(error => {
-      setLoading(false);
-      showDataFetchError((error as AxiosError).toString());
-    });
-  }
+    }
+  }, [containersData.data]);
 
   function handleColumnChange(selected: ValueType<Option, true>) {
     setSelectedColumns(selected as Option[]);
   }
 
-  const autoReloadHelper: AutoReloadHelper = new AutoReloadHelper(loadData);
+  function handleTagClose(label: string) {
+    setSelectedColumns(
+      selectedColumns.filter((column) => column.label !== label)
+    );
+  }
+
+  function handleTabChange(key: string) {
+    setSelectedTab(key);
+  }
 
-  React.useEffect(() => {
-    autoReloadHelper.startPolling();
-    loadData();
+  // Create refresh function for auto-reload
+  const loadContainersData = () => {
+    containersData.refetch();
+  };
 
-    return (() => {
-      autoReloadHelper.stopPolling();
-      cancelRequests([cancelSignal.current!])
-    })
-  }, []);
+  const autoReload = useAutoReload(loadContainersData);
 
   const {
-    lastUpdated, columnOptions,
-    missingContainerData, underReplicatedContainerData,
-    overReplicatedContainerData, misReplicatedContainerData, 
mismatchedReplicaContainerData
+    lastUpdated,
+    columnOptions,
+    missingContainerData,
+    underReplicatedContainerData,
+    overReplicatedContainerData,
+    misReplicatedContainerData,
+    mismatchedReplicaContainerData
   } = state;
 
   // Mapping the data to the Tab keys for enabling/disabling search
@@ -181,22 +186,39 @@ const Containers: React.FC<{}> = () => {
     </div>
   )
 
+  const getCurrentTabData = () => {
+    switch (selectedTab) {
+      case '1':
+        return missingContainerData;
+      case '2':
+        return underReplicatedContainerData;
+      case '3':
+        return overReplicatedContainerData;
+      case '4':
+        return misReplicatedContainerData;
+      case '5':
+        return mismatchedReplicaContainerData;
+      default:
+        return missingContainerData;
+    }
+  };
+
   return (
     <>
       <div className='page-header-v2'>
         Containers
         <AutoReloadPanel
-          isLoading={loading}
+          isLoading={containersData.loading}
           lastRefreshed={lastUpdated}
-          togglePolling={autoReloadHelper.handleAutoReloadToggle}
-          onReload={loadData}
+          togglePolling={autoReload.handleAutoReloadToggle}
+          onReload={loadContainersData}
         />
       </div>
       <div style={{ padding: '24px' }}>
         <div style={{ marginBottom: '12px' }}>
           <Card
             title='Highlights'
-            loading={loading}>
+            loading={containersData.loading}>
               <Row
                 align='middle'>
                   {highlightData}
@@ -236,7 +258,7 @@ const Containers: React.FC<{}> = () => {
               tab='Missing'>
               <ContainerTable
                 data={missingContainerData}
-                loading={loading}
+                loading={containersData.loading}
                 searchColumn={searchColumn}
                 searchTerm={debouncedSearch}
                 selectedColumns={selectedColumns}
@@ -249,7 +271,7 @@ const Containers: React.FC<{}> = () => {
               tab='Under-Replicated'>
               <ContainerTable
                 data={underReplicatedContainerData}
-                loading={loading}
+                loading={containersData.loading}
                 searchColumn={searchColumn}
                 searchTerm={debouncedSearch}
                 selectedColumns={selectedColumns}
@@ -262,7 +284,7 @@ const Containers: React.FC<{}> = () => {
               tab='Over-Replicated'>
               <ContainerTable
                 data={overReplicatedContainerData}
-                loading={loading}
+                loading={containersData.loading}
                 searchColumn={searchColumn}
                 searchTerm={debouncedSearch}
                 selectedColumns={selectedColumns}
@@ -275,7 +297,7 @@ const Containers: React.FC<{}> = () => {
               tab='Mis-Replicated'>
               <ContainerTable
                 data={misReplicatedContainerData}
-                loading={loading}
+                loading={containersData.loading}
                 searchColumn={searchColumn}
                 searchTerm={debouncedSearch}
                 selectedColumns={selectedColumns}
@@ -288,7 +310,7 @@ const Containers: React.FC<{}> = () => {
               tab='Mismatched Replicas'>
               <ContainerTable
                 data={mismatchedReplicaContainerData}
-                loading={loading}
+                loading={containersData.loading}
                 searchColumn={searchColumn}
                 searchTerm={debouncedSearch}
                 selectedColumns={selectedColumns}
@@ -303,4 +325,4 @@ const Containers: React.FC<{}> = () => {
   );
 }
 
-export default Containers;
\ No newline at end of file
+export default Containers;
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
index 7c044b2ae1e..101db9d4b03 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
@@ -22,7 +22,6 @@ import React, {
   useState
 } from 'react';
 import moment from 'moment';
-import { AxiosError } from 'axios';
 import {
   Button,
   Modal
@@ -38,12 +37,7 @@ import MultiSelect, { Option } from 
'@/v2/components/select/multiSelect';
 import DatanodesTable, { COLUMNS } from 
'@/v2/components/tables/datanodesTable';
 import AutoReloadPanel from '@/components/autoReloadPanel/autoReloadPanel';
 import { showDataFetchError } from '@/utils/common';
-import { AutoReloadHelper } from '@/utils/autoReloadHelper';
-import {
-  AxiosGetHelper,
-  AxiosPutHelper,
-  cancelRequests
-} from '@/utils/axiosRequestHelper';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
 
 import { useDebounce } from '@/v2/hooks/useDebounce';
 import {
@@ -55,7 +49,12 @@ import {
 } from '@/v2/types/datanode.types';
 
 import './datanodes.less'
+import { useAutoReload } from '@/v2/hooks/useAutoReload.hook';
 
+// Type for decommission API response
+type DecommissionAPIResponse = {
+  DatanodesDecommissionInfo: DatanodeDecomissionInfo[];
+};
 
 const defaultColumns = COLUMNS.map(column => ({
   label: (typeof column.title === 'string')
@@ -80,15 +79,46 @@ const COLUMN_UPDATE_DECOMMISSIONING = 'DECOMMISSIONING';
 
 const Datanodes: React.FC<{}> = () => {
 
-  const cancelSignal = useRef<AbortController>();
-  const cancelDecommissionSignal = useRef<AbortController>();
-
   const [state, setState] = useState<DatanodesState>({
     lastUpdated: 0,
     columnOptions: defaultColumns,
     dataSource: []
   });
-  const [loading, setLoading] = useState<boolean>(false);
+  
+  // API hooks for data fetching
+  const decommissionAPI = useApiData<DecommissionAPIResponse>(
+    '/api/v1/datanodes/decommission/info',
+    { DatanodesDecommissionInfo: [] },
+    { 
+      initialFetch: false,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+  
+  const datanodesAPI = useApiData<DatanodesResponse>(
+    '/api/v1/datanodes',
+    { datanodes: [], totalCount: 0 },
+    { 
+      initialFetch: false,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+  
+  const removeDatanodesAPI = useApiData<any>(
+    '/api/v1/datanodes/remove',
+    null,
+    { 
+      method: 'PUT',
+      initialFetch: false,
+      onError: (error) => showDataFetchError(error),
+      onSuccess: () => {
+        loadData();
+        setSelectedRows([]);
+      }
+    }
+  );
+  
+  const loading = decommissionAPI.loading || datanodesAPI.loading || 
removeDatanodesAPI.loading;
   const [selectedColumns, setSelectedColumns] = 
useState<Option[]>(defaultColumns);
   const [selectedRows, setSelectedRows] = useState<React.Key[]>([]);
   const [searchTerm, setSearchTerm] = useState<string>('');
@@ -101,62 +131,31 @@ const Datanodes: React.FC<{}> = () => {
     setSelectedColumns(selected as Option[]);
   }
 
-  async function loadDecommisionAPI() {
-    decommissionUuids = [];
-    const { request, controller } = await AxiosGetHelper(
-      '/api/v1/datanodes/decommission/info',
-      cancelDecommissionSignal.current
-    );
-    cancelDecommissionSignal.current = controller;
-    return request
-  };
-
-  async function loadDataNodeAPI() {
-    const { request, controller } = await AxiosGetHelper(
-      '/api/v1/datanodes',
-      cancelSignal.current
-    );
-    cancelSignal.current = controller;
-    return request;
-  };
-
   async function removeDatanode(selectedRowKeys: string[]) {
-    setLoading(true);
-    const { request, controller } = await AxiosPutHelper(
-      '/api/v1/datanodes/remove',
-      selectedRowKeys,
-      cancelSignal.current
-    );
-    cancelSignal.current = controller;
-    request.then(() => {
-      loadData();
-    }).catch((error) => {
-      showDataFetchError(error.toString());
-    }).finally(() => {
-      setLoading(false);
-      setSelectedRows([]);
-    });
-  }
-
-  const loadData = async () => {
-    setLoading(true);
-    // Need to call decommission API on each interval to get updated status
-    // before datanode API call to compare UUID's
-    // update 'Operation State' column in table manually before rendering
     try {
-      let decomissionResponse = await loadDecommisionAPI();
-      decommissionUuids = 
decomissionResponse.data?.DatanodesDecommissionInfo?.map(
-        (item: DatanodeDecomissionInfo) => item.datanodeDetails.uuid
-      );
+      await removeDatanodesAPI.execute(selectedRowKeys);
     } catch (error) {
-      decommissionUuids = [];
-      showDataFetchError((error as AxiosError).toString());
+      showDataFetchError(error);
     }
+  }
 
-    try {
-      const datanodesAPIResponse = await loadDataNodeAPI();
-      const datanodesResponse: DatanodesResponse = datanodesAPIResponse.data;
-      const datanodes: DatanodeResponse[] = datanodesResponse.datanodes;
+  const loadData = () => {
+    // Trigger both API hooks to refetch data
+    decommissionAPI.refetch();
+    datanodesAPI.refetch();
+  };
+
+  // Process data when both APIs have loaded
+  useEffect(() => {
+    if (!decommissionAPI.loading && !datanodesAPI.loading && 
+        decommissionAPI.data && datanodesAPI.data) {
+      
+      // Update decommission UUIDs
+      decommissionUuids = decommissionAPI.data?.DatanodesDecommissionInfo?.map(
+        (item: DatanodeDecomissionInfo) => item.datanodeDetails.uuid
+      ) || [];
+
+      const datanodes: DatanodeResponse[] = datanodesAPI.data.datanodes;
       const dataSource: Datanode[] = datanodes?.map(
         (datanode) => ({
           hostname: datanode.hostname,
@@ -181,30 +180,22 @@ const Datanodes: React.FC<{}> = () => {
           networkLocation: datanode.networkLocation
         })
       );
-      setLoading(false);
+
       setState({
         ...state,
         dataSource: dataSource,
         lastUpdated: Number(moment())
       });
-    } catch (error) {
-      setLoading(false);
-      showDataFetchError((error as AxiosError).toString())
     }
-  }
+  }, [decommissionAPI.loading, datanodesAPI.loading, decommissionAPI.data, 
datanodesAPI.data]);
 
-  const autoReloadHelper: AutoReloadHelper = new AutoReloadHelper(loadData);
+  const autoReload = useAutoReload(loadData);
 
   useEffect(() => {
-    autoReloadHelper.startPolling();
-    loadData();
+    autoReload.startPolling();
 
     return (() => {
-      autoReloadHelper.stopPolling();
-      cancelRequests([
-        cancelSignal.current!,
-        cancelDecommissionSignal.current!
-      ]);
+      autoReload.stopPolling();
     });
   }, []);
 
@@ -231,7 +222,7 @@ const Datanodes: React.FC<{}> = () => {
         <AutoReloadPanel
           isLoading={loading}
           lastRefreshed={lastUpdated}
-          togglePolling={autoReloadHelper.handleAutoReloadToggle}
+          togglePolling={autoReload.handleAutoReloadToggle}
           onReload={loadData} />
       </div>
       <div className='data-container'>
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/heatmap/heatmap.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/heatmap/heatmap.tsx
index c243cee918d..8895a1dc3e2 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/heatmap/heatmap.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/heatmap/heatmap.tsx
@@ -15,15 +15,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, { ChangeEvent, useRef, useState } from 'react';
+import React, { ChangeEvent, useState, useEffect, useCallback } from 'react';
 import moment, { Moment } from 'moment';
 import { Button, Menu, Input, Dropdown, DatePicker, Form, Result, Spin } from 
'antd';
 import { MenuProps } from 'antd/es/menu';
 import { DownOutlined } from '@ant-design/icons';
 
-
 import { showDataFetchError } from '@/utils/common';
-import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
 import * as CONSTANTS from '@/v2/constants/heatmap.constants';
 import { HeatmapChild, HeatmapResponse, HeatmapState, InputPathState, 
InputPathValidTypes, IResponseError } from '@/v2/types/heatmap.types';
 import HeatmapPlot from '@/v2/components/plots/heatmapPlot';
@@ -34,17 +33,22 @@ import { useLocation } from 'react-router-dom';
 let minSize = Infinity;
 let maxSize = 0;
 
-const Heatmap: React.FC<{}> = () => {
+const DEFAULT_HEATMAP_RESPONSE: HeatmapResponse = {
+  label: '',
+  path: '',
+  children: [],
+  size: 0,
+  maxAccessCount: 0,
+  minAccessCount: 0
+};
 
+const DEFAULT_DISABLED_FEATURES_RESPONSE = {
+  data: []
+};
+
+const Heatmap: React.FC<{}> = () => {
   const [state, setState] = useState<HeatmapState>({
-    heatmapResponse: {
-      label: '',
-      path: '',
-      children: [],
-      size: 0,
-      maxAccessCount: 0,
-      minAccessCount: 0
-    },
+    heatmapResponse: DEFAULT_HEATMAP_RESPONSE,
     entityType: CONSTANTS.ENTITY_TYPES[0],
     date: CONSTANTS.TIME_PERIODS[0]
   });
@@ -55,15 +59,64 @@ const Heatmap: React.FC<{}> = () => {
     helpMessage: ''
   });
 
-  const [isLoading, setLoading] = useState<boolean>(false);
+  const [searchPath, setSearchPath] = useState<string>(CONSTANTS.ROOT_PATH);
   const [treeEndpointFailed, setTreeEndpointFailed] = useState<boolean>(false);
 
   const location = useLocation();
-  const cancelSignal = useRef<AbortController>();
-  const cancelDisabledFeatureSignal = useRef<AbortController>();
+  const [isHeatmapEnabled, setIsHeatmapEnabled] = 
useState<boolean>((location?.state as any)?.isHeatmapEnabled);
+
+  // Use the modern hooks pattern for heatmap data - only trigger on 
searchPath change
+  const heatmapData = useApiData<HeatmapResponse>(
+    isHeatmapEnabled && state.date && searchPath && state.entityType
+      ? 
`/api/v1/heatmap/readaccess?startDate=${state.date}&path=${searchPath}&entityType=${state.entityType}`
+      : '',
+    DEFAULT_HEATMAP_RESPONSE,
+    {
+      retryAttempts: 2,
+      onError: (error: any) => {
+        if (error.response?.status !== 404) {
+          showDataFetchError(error.message.toString());
+        }
+        setTreeEndpointFailed(true);
+        setInputPathState(prevState => ({
+          ...prevState,
+          inputPath: CONSTANTS.ROOT_PATH
+        }));
+        setSearchPath(CONSTANTS.ROOT_PATH);
+      }
+    }
+  );
+
+  // Use the modern hooks pattern for disabled features
+  const disabledFeaturesData = useApiData<{ data: string[] }>(
+    '/api/v1/features/disabledFeatures',
+    DEFAULT_DISABLED_FEATURES_RESPONSE,
+    {
+      retryAttempts: 2,
+      onError: (error: any) => showDataFetchError(error)
+    }
+  );
 
-  const [isHeatmapEnabled, setIsHeatmapEnabled] = 
useState<boolean>(location?.state?.isHeatmapEnabled);
+  // Process heatmap data when it changes
+  useEffect(() => {
+    if (heatmapData.data && heatmapData.data.label !== '') {
+      minSize = heatmapData.data.minAccessCount;
+      maxSize = heatmapData.data.maxAccessCount;
+      const heatmapResponse: HeatmapResponse = updateSize(heatmapData.data);
+      setState(prevState => ({
+        ...prevState,
+        heatmapResponse: heatmapResponse
+      }));
+      setTreeEndpointFailed(false);
+    }
+  }, [heatmapData.data]);
 
+  // Process disabled features data when it changes
+  useEffect(() => {
+    if (disabledFeaturesData.data && disabledFeaturesData.data.data) {
+      setIsHeatmapEnabled(!disabledFeaturesData.data.data.includes('HEATMAP'));
+    }
+  }, [disabledFeaturesData.data]);
 
   function handleChange(e: ChangeEvent<HTMLInputElement>) {
     const value = e.target.value;
@@ -84,7 +137,9 @@ const Heatmap: React.FC<{}> = () => {
   }
 
   function handleSubmit() {
-    updateHeatmap(inputPathState.inputPath, state.entityType, state.date);
+    if (isHeatmapEnabled && state.date && inputPathState.inputPath && 
state.entityType) {
+      setSearchPath(inputPathState.inputPath);
+    }
   }
 
   const normalize = (min: number, max: number, size: number) => {
@@ -117,17 +172,17 @@ const Heatmap: React.FC<{}> = () => {
 
       // hide block at key,volume,bucket level if size accessCount and 
maxAccessCount are zero apply normalized size only for leaf level
       if ((obj as HeatmapChild)?.size === 0 && (obj as 
HeatmapChild)?.accessCount === 0) {
-        obj['normalizedSize'] = 0;
+        (obj as any)['normalizedSize'] = 0;
       } else if ((obj as HeatmapResponse)?.size === 0 && (obj as 
HeatmapResponse)?.maxAccessCount === 0) {
-        obj['normalizedSize'] = 0;
+        (obj as any)['normalizedSize'] = 0;
       }
       else if (obj?.size === 0 && ((obj as HeatmapChild)?.accessCount >= 0 || 
(obj as HeatmapResponse).maxAccessCount >= 0)) {
-        obj['normalizedSize'] = 1;
+        (obj as any)['normalizedSize'] = 1;
         obj.size = 0;
       }
       else {
         const newSize = normalize(minSize, maxSize, obj.size);
-        obj['normalizedSize'] = newSize;
+        (obj as any)['normalizedSize'] = newSize;
       }
     }
 
@@ -137,89 +192,18 @@ const Heatmap: React.FC<{}> = () => {
     return obj as HeatmapResponse;
   };
 
-  const updateHeatmap = (path: string, entityType: string, date: string | 
number) => {
-    // Only perform requests if the heatmap is enabled
-    if (isHeatmapEnabled) {
-      setLoading(true);
-      // We want to ensure these are not empty as they will be passed as path 
params
-      if (date && path && entityType) {
-        const { request, controller } = AxiosGetHelper(
-          
`/api/v1/heatmap/readaccess?startDate=${date}&path=${path}&entityType=${entityType}`,
-          cancelSignal.current
-        );
-        cancelSignal.current = controller;
-
-        request.then(response => {
-          if (response?.status === 200) {
-            minSize = response.data.minAccessCount;
-            maxSize = response.data.maxAccessCount;
-            const heatmapResponse: HeatmapResponse = updateSize(response.data);
-            setLoading(false);
-            setState(prevState => ({
-              ...prevState,
-              heatmapResponse: heatmapResponse
-            }));
-          } else {
-            const error = new Error((response.status).toString()) as 
IResponseError;
-            error.status = response.status;
-            error.message = `Failed to fetch Heatmap Response with status 
${error.status}`
-            throw error;
-          }
-        }).catch(error => {
-          setLoading(false);
-          setInputPathState(prevState => ({
-            ...prevState,
-            inputPath: CONSTANTS.ROOT_PATH
-          }));
-          setTreeEndpointFailed(true);
-          if (error.response.status !== 404) {
-            showDataFetchError(error.message.toString());
-          }
-        });
-      } else {
-        setLoading(false);
-      }
-
-    }
-  }
-
   const updateHeatmapParent = (path: string) => {
     setInputPathState(prevState => ({
       ...prevState,
       inputPath: path
     }));
+    setSearchPath(path);
   }
 
   function isDateDisabled(current: Moment) {
     return current > moment() || current < moment().subtract(90, 'day');
   }
 
-  function getIsHeatmapEnabled() {
-    const disabledfeaturesEndpoint = `/api/v1/features/disabledFeatures`;
-    const { request, controller } = AxiosGetHelper(
-      disabledfeaturesEndpoint,
-      cancelDisabledFeatureSignal.current
-    )
-    cancelDisabledFeatureSignal.current = controller;
-    request.then(response => {
-      setIsHeatmapEnabled(!response?.data?.includes('HEATMAP'));
-    }).catch(error => {
-      showDataFetchError((error as Error).toString());
-    });
-  }
-
-  React.useEffect(() => {
-    // We do not know if heatmap is enabled or not, so set it
-    if (isHeatmapEnabled === undefined) {
-      getIsHeatmapEnabled();
-    }
-    updateHeatmap(inputPathState.inputPath, state.entityType, state.date);
-
-    return (() => {
-      cancelSignal.current && cancelSignal.current.abort();
-    })
-  }, [isHeatmapEnabled, state.entityType, state.date]);
-
   const handleDatePickerChange = (date: moment.MomentInput) => {
     setState(prevState => ({
       ...prevState,
@@ -249,6 +233,7 @@ const Heatmap: React.FC<{}> = () => {
 
   const { date, entityType, heatmapResponse } = state;
   const { inputPath, helpMessage, isInputPathValid } = inputPathState;
+  const loading = heatmapData.loading || disabledFeaturesData.loading;
 
   const menuCalendar = (
     <Menu
@@ -363,7 +348,7 @@ const Heatmap: React.FC<{}> = () => {
                   </div>
                 </div>
               </div>
-              {isLoading
+              {loading
                 ? <Spin size='large' />
                 : (Object.keys(heatmapResponse).length > 0 && 
(heatmapResponse.label !== null || heatmapResponse.path !== null))
                   ? <div id="heatmap-plot-container">
@@ -384,4 +369,4 @@ const Heatmap: React.FC<{}> = () => {
   );
 }
 
-export default Heatmap;
\ No newline at end of file
+export default Heatmap;
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.tsx
index f2a2c3e3f7d..09bf62f288c 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/insights.tsx
@@ -16,15 +16,11 @@
  * limitations under the License.
  */
 
-import React, { useState } from 'react';
-import axios, {
-  CanceledError,
-  AxiosError
-} from 'axios';
+import React, { useState, useEffect } from 'react';
 import { Row, Col, Card, Result } from 'antd';
 
 import { showDataFetchError } from '@/utils/common';
-import { PromiseAllSettledGetHelper } from '@/utils/axiosRequestHelper';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
 
 import { Option } from '@/v2/components/select/multiSelect';
 import FileSizeDistribution from '@/v2/components/plots/insightsFilePlot';
@@ -38,7 +34,6 @@ import {
 
 const Insights: React.FC<{}> = () => {
 
-  const [loading, setLoading] = useState<boolean>(false);
   const [state, setState] = useState<InsightsState>({
     volumeBucketMap: new Map<string, Set<string>>(),
     volumeOptions: [],
@@ -58,95 +53,89 @@ const Insights: React.FC<{}> = () => {
     }]
   });
 
-  const cancelInsightSignal = React.useRef<AbortController>();
-
-  function loadData() {
-    setLoading(true);
-    const { requests, controller } = PromiseAllSettledGetHelper([
-      '/api/v1/utilization/fileCount',
-      '/api/v1/utilization/containerCount'
-    ], cancelInsightSignal.current);
-
-    cancelInsightSignal.current = controller;
-    requests.then(axios.spread((
-      fileCountResponse: Awaited<Promise<any>>,
-      containerCountResponse: Awaited<Promise<any>>
-    ) => {
-      let fileAPIError;
-      let containerAPIError;
-      let responseError = [
-        fileCountResponse,
-        containerCountResponse
-      ].filter((resp) => resp.status === 'rejected');
-
-      if (responseError.length !== 0) {
-        responseError.forEach((err) => {
-          if (err.reason.toString().includes('CancelledError')) {
-            throw new CanceledError('canceled', 'ERR_CANCELED');
-          } else {
-            if (err.reason.config.url.includes("fileCount")) {
-              fileAPIError = err.reason.toString();
+  // Individual API calls
+  const fileCountAPI = useApiData<FileCountResponse[]>(
+    '/api/v1/utilization/fileCount',
+    [],
+    {
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+
+  const containerCountAPI = useApiData<any[]>(
+    '/api/v1/utilization/containerCount',
+    [],
+    {
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+
+  const loading = fileCountAPI.loading || containerCountAPI.loading;
+
+  // Process the API responses when they're available
+  useEffect(() => {
+    if (!fileCountAPI.loading && !containerCountAPI.loading && 
+        fileCountAPI.data && containerCountAPI.data) {
+      
+      // Extract errors
+      const fileAPIError = fileCountAPI.error;
+      const containerAPIError = containerCountAPI.error;
+
+      // Process fileCount response only if successful
+      let volumeBucketMap = new Map<string, Set<string>>();
+      let volumeOptions: Option[] = [];
+      
+      if (fileCountAPI.data && fileCountAPI.data.length > 0) {
+        // Construct volume -> bucket[] map for populating filters
+        volumeBucketMap = fileCountAPI.data.reduce(
+          (map: Map<string, Set<string>>, current: FileCountResponse) => {
+            const volume = current.volume;
+            const bucket = current.bucket;
+            if (map.has(volume)) {
+              const buckets = Array.from(map.get(volume)!);
+              map.set(volume, new Set<string>([...buckets, bucket]));
             } else {
-              containerAPIError = err.reason.toString();
+              map.set(volume, new Set<string>().add(bucket));
             }
-          }
-        });
+            return map;
+          },
+          new Map<string, Set<string>>()
+        );
+        volumeOptions = Array.from(volumeBucketMap.keys()).map(k => ({
+          label: k,
+          value: k
+        }));
       }
 
-      // Construct volume -> bucket[] map for populating filters
-      // Ex: vol1 -> [bucket1, bucket2], vol2 -> [bucket1]
-      const volumeBucketMap: Map<string, Set<string>> = 
fileCountResponse.value?.data?.reduce(
-        (map: Map<string, Set<string>>, current: FileCountResponse) => {
-          const volume = current.volume;
-          const bucket = current.bucket;
-          if (map.has(volume)) {
-            const buckets = Array.from(map.get(volume)!);
-            map.set(volume, new Set<string>([...buckets, bucket]));
-          } else {
-            map.set(volume, new Set<string>().add(bucket));
-          }
-          return map;
-        },
-        new Map<string, Set<string>>()
-      );
-      const volumeOptions: Option[] = Array.from(volumeBucketMap.keys()).map(k 
=> ({
-        label: k,
-        value: k
-      }));
-
       setState({
         ...state,
-        volumeBucketMap: volumeBucketMap,
-        volumeOptions: volumeOptions,
-        fileCountError: fileAPIError,
-        containerSizeError: containerAPIError
+        volumeBucketMap,
+        volumeOptions,
+        fileCountError: fileAPIError || undefined,
+        containerSizeError: containerAPIError || undefined
       });
+      
       setPlotResponse({
-        fileCountResponse: fileCountResponse.value?.data ?? [{
+        fileCountResponse: fileCountAPI.data || [{
           volume: '',
           bucket: '',
           fileSize: 0,
           count: 0
         }],
-        containerCountResponse: containerCountResponse.value?.data ?? [{
+        containerCountResponse: containerCountAPI.data || [{
           containerSize: 0,
           count: 0
         }]
       });
-      setLoading(false);
-    })).catch(error => {
-      setLoading(false);
-      showDataFetchError((error as AxiosError).toString());
-    })
-  }
-
-  React.useEffect(() => {
-    loadData();
-
-    return (() => {
-      cancelInsightSignal.current && cancelInsightSignal.current.abort();
-    })
-  }, []);
+    }
+  }, [
+    fileCountAPI.loading,
+    containerCountAPI.loading,
+    fileCountAPI.data,
+    containerCountAPI.data,
+    fileCountAPI.error,
+    containerCountAPI.error
+  ]);  
 
   return (
     <>
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx
index 732af0aa00e..7c300ff9dd6 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/insights/omInsights.tsx
@@ -17,7 +17,6 @@
  */
 
 import React from 'react';
-import { AxiosError } from 'axios';
 import { ValueType } from 'react-select';
 import { Tabs, Tooltip } from 'antd';
 import { TablePaginationConfig } from 'antd/es/table';
@@ -87,7 +86,7 @@ const OMDBInsights: React.FC<{}> = () => {
         ));
         setLoading(false);
       }).catch(error => {
-        showDataFetchError((error as AxiosError).toString());
+        showDataFetchError(error);
       });
     }
   }
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/namespaceUsage/namespaceUsage.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/namespaceUsage/namespaceUsage.tsx
index f7fa6c13bba..ab652225d4c 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/namespaceUsage/namespaceUsage.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/namespaceUsage/namespaceUsage.tsx
@@ -16,8 +16,7 @@
  * limitations under the License.
  */
 
-import React, { useRef, useState } from 'react';
-import { AxiosError } from 'axios';
+import React, { useState } from 'react';
 import { Alert, Button, Tooltip } from 'antd';
 import { InfoCircleFilled, ReloadOutlined, } from '@ant-design/icons';
 import { ValueType } from 'react-select';
@@ -27,7 +26,7 @@ import NUPieChart from '@/v2/components/plots/nuPieChart';
 import SingleSelect, { Option } from '@/v2/components/select/singleSelect';
 import DUBreadcrumbNav from '@/v2/components/duBreadcrumbNav/duBreadcrumbNav';
 import { showDataFetchError, showInfoNotification } from '@/utils/common';
-import { AxiosGetHelper, cancelRequests } from '@/utils/axiosRequestHelper';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
 
 import { NUResponse } from '@/v2/types/namespaceUsage.types';
 
@@ -41,37 +40,38 @@ const LIMIT_OPTIONS: Option[] = [
   { label: '30', value: '30' }
 ]
 
+const DEFAULT_NU_RESPONSE: NUResponse = {
+  status: '',
+  path: '/',
+  subPathCount: 0,
+  size: 0,
+  sizeWithReplica: 0,
+  subPaths: [],
+  sizeDirectKey: 0
+};
+
 const NamespaceUsage: React.FC<{}> = () => {
-  const [loading, setLoading] = useState<boolean>(false);
   const [limit, setLimit] = useState<Option>(LIMIT_OPTIONS[1]);
-  const [duResponse, setDUResponse] = useState<NUResponse>({
-    status: '',
-    path: '/',
-    subPathCount: 0,
-    size: 0,
-    sizeWithReplica: 0,
-    subPaths: [],
-    sizeDirectKey: 0
-  });
-
-  const cancelPieSignal = useRef<AbortController>();
-
-  function loadData(path: string) {
-    console.log("Loading data at: ", path);
-    setLoading(true);
-    const { request, controller } = AxiosGetHelper(
-      `/api/v1/namespace/usage?path=${path}&files=true&sortSubPaths=true`,
-      cancelPieSignal.current
-    );
-    cancelPieSignal.current = controller;
+  const [currentPath, setCurrentPath] = useState<string>('/');
+
+  // Use the modern hooks pattern
+  const namespaceUsageData = useApiData<NUResponse>(
+    `/api/v1/namespace/usage?path=${currentPath}&files=true&sortSubPaths=true`,
+    DEFAULT_NU_RESPONSE,
+    {
+      retryAttempts: 2,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
 
-    request.then(response => {
-      const duResponse: NUResponse = response.data;
-      console.log(duResponse);
+  // Process data when it changes
+  React.useEffect(() => {
+    if (namespaceUsageData.data) {
+      const duResponse = namespaceUsageData.data;
       const status = duResponse.status;
+      
       if (status === 'PATH_NOT_FOUND') {
-        setLoading(false);
-        showDataFetchError(`Invalid Path: ${path}`);
+        showDataFetchError(`Invalid Path: ${currentPath}`);
         return;
       }
 
@@ -79,27 +79,19 @@ const NamespaceUsage: React.FC<{}> = () => {
         showInfoNotification("Information being initialized", "Namespace 
Summary is being initialized, please wait.")
         return;
       }
+    }
+  }, [namespaceUsageData.data, currentPath]);
 
-      setDUResponse(duResponse);
-      setLoading(false);
-    }).catch(error => {
-      setLoading(false);
-      showDataFetchError((error as AxiosError).toString());
-    });
-  }
+  const loadData = (path: string) => {
+    setCurrentPath(path);
+  };
 
   function handleLimitChange(selected: ValueType<Option, false>) {
     setLimit(selected as Option);
   }
 
-  React.useEffect(() => {
-    //On mount load default data
-    loadData(duResponse.path)
-
-    return (() => {
-      cancelRequests([cancelPieSignal.current!]);
-    })
-  }, []);
+  const duResponse = namespaceUsageData.data || DEFAULT_NU_RESPONSE;
+  const loading = namespaceUsageData.loading;
 
   return (
     <>
@@ -149,7 +141,7 @@ const NamespaceUsage: React.FC<{}> = () => {
               subPaths={duResponse.subPaths}
               sizeWithReplica={duResponse.sizeWithReplica}
               size={duResponse.size} />
-            <NUMetadata path={duResponse.path} />
+            <NUMetadata path={currentPath} />
           </div>
         </div>
       </div>
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/pipelines/pipelines.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/pipelines/pipelines.tsx
index 19a3c9447a6..a99d300d4d8 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/pipelines/pipelines.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/pipelines/pipelines.tsx
@@ -16,11 +16,7 @@
  * limitations under the License.
  */
 
-import React, {
-  useEffect,
-  useRef,
-  useState
-} from 'react';
+import React, { useEffect, useState, useCallback } from 'react';
 import moment from 'moment';
 import { ValueType } from 'react-select';
 
@@ -29,9 +25,9 @@ import Search from '@/v2/components/search/search';
 import MultiSelect, { Option } from '@/v2/components/select/multiSelect';
 import PipelinesTable, { COLUMNS } from 
'@/v2/components/tables/pipelinesTable';
 import { showDataFetchError } from '@/utils/common';
-import { AutoReloadHelper } from '@/utils/autoReloadHelper';
-import { AxiosGetHelper, cancelRequests } from '@/utils/axiosRequestHelper';
 import { useDebounce } from '@/v2/hooks/useDebounce';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
+import { useAutoReload } from '@/v2/hooks/useAutoReload.hook';
 
 import {
   Pipeline,
@@ -41,7 +37,6 @@ import {
 
 import './pipelines.less';
 
-
 const defaultColumns = COLUMNS.map(column => ({
   label: (typeof column.title === 'string')
     ? column.title
@@ -49,76 +44,74 @@ const defaultColumns = COLUMNS.map(column => ({
   value: column.key as string,
 }));
 
-const Pipelines: React.FC<{}> = () => {
-  const cancelSignal = useRef<AbortController>();
+const DEFAULT_PIPELINES_RESPONSE: PipelinesResponse = {
+  totalCount: 0,
+  pipelines: []
+};
 
+const Pipelines: React.FC<{}> = () => {
   const [state, setState] = useState<PipelinesState>({
     activeDataSource: [],
     columnOptions: defaultColumns,
     lastUpdated: 0,
   });
-  const [loading, setLoading] = useState<boolean>(false);
   const [selectedColumns, setSelectedColumns] = 
useState<Option[]>(defaultColumns);
   const [searchTerm, setSearchTerm] = useState<string>('');
 
   const debouncedSearch = useDebounce(searchTerm, 300);
 
-  const loadData = () => {
-    setLoading(true);
-    //Cancel any previous requests
-    cancelRequests([cancelSignal.current!]);
-
-    const { request, controller } = AxiosGetHelper(
-      '/api/v1/pipelines',
-      cancelSignal.current
-    );
+  // Use the modern hooks pattern
+  const pipelinesData = useApiData<PipelinesResponse>(
+    '/api/v1/pipelines',
+    DEFAULT_PIPELINES_RESPONSE,
+    {
+      retryAttempts: 2,
+      initialFetch: false,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
 
-    cancelSignal.current = controller;
-    request.then(response => {
-      const pipelinesResponse: PipelinesResponse = response.data;
-      const pipelines: Pipeline[] = pipelinesResponse?.pipelines ?? {};
+  // Process pipelines data when it changes
+  useEffect(() => {
+    if (pipelinesData.data && pipelinesData.data.pipelines) {
+      const pipelines: Pipeline[] = pipelinesData.data.pipelines;
       setState({
         ...state,
         activeDataSource: pipelines,
         lastUpdated: Number(moment())
-      })
-      setLoading(false);
-    }).catch(error => {
-      setLoading(false);
-      showDataFetchError(error.toString());
-    })
-  }
-
-  const autoReloadHelper: AutoReloadHelper = new AutoReloadHelper(loadData);
-
-  useEffect(() => {
-    autoReloadHelper.startPolling();
-    loadData();
-    return (() => {
-      autoReloadHelper.stopPolling();
-      cancelRequests([cancelSignal.current!]);
-    })
-  }, []);
+      });
+    }
+  }, [pipelinesData.data]);
 
   function handleColumnChange(selected: ValueType<Option, true>) {
     setSelectedColumns(selected as Option[]);
   }
 
-  const {
-    activeDataSource,
-    columnOptions,
-    lastUpdated
-  } = state;
+  function handleTagClose(label: string) {
+    setSelectedColumns(
+      selectedColumns.filter((column) => column.label !== label)
+    );
+  }
+
+  // Create refresh function for auto-reload
+  const loadPipelinesData = () => {
+    pipelinesData.refetch();
+  };
+
+  const autoReload = useAutoReload(loadPipelinesData);
+
+  const { activeDataSource, lastUpdated, columnOptions } = state;
 
   return (
     <>
       <div className='page-header-v2'>
         Pipelines
         <AutoReloadPanel
-          isLoading={loading}
+          isLoading={pipelinesData.loading}
           lastRefreshed={lastUpdated}
-          togglePolling={autoReloadHelper.handleAutoReloadToggle}
-          onReload={loadData} />
+          togglePolling={autoReload.handleAutoReloadToggle}
+          onReload={loadPipelinesData}
+        />
       </div>
       <div className='data-container'>
         <div className='content-div'>
@@ -130,7 +123,7 @@ const Pipelines: React.FC<{}> = () => {
                 selected={selectedColumns}
                 placeholder='Columns'
                 onChange={handleColumnChange}
-                onTagClose={() => { }}
+                onTagClose={handleTagClose}
                 fixedColumn='pipelineId'
                 columnLength={COLUMNS.length} />
             </div>
@@ -141,14 +134,14 @@ const Pipelines: React.FC<{}> = () => {
                 value: 'pipelineId'
               }]}
               searchInput={searchTerm}
-              searchColumn={'pipelineId'}
+              searchColumn='pipelineId'
               onSearchChange={
                 (e: React.ChangeEvent<HTMLInputElement>) => 
setSearchTerm(e.target.value)
               }
-              onChange={() => { }} />
+              onChange={() => setSearchTerm('')} />
           </div>
           <PipelinesTable
-            loading={loading}
+            loading={pipelinesData.loading}
             data={activeDataSource}
             selectedColumns={selectedColumns}
             searchTerm={debouncedSearch} />
@@ -157,4 +150,5 @@ const Pipelines: React.FC<{}> = () => {
     </>
   );
 }
-export default Pipelines;
\ No newline at end of file
+
+export default Pipelines;
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
index a10b71282ce..9188eae8e17 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useEffect, useState, useCallback } from 'react';
 import moment from 'moment';
 import { ValueType } from 'react-select/src/types';
 
@@ -28,10 +28,10 @@ import VolumesTable, { COLUMNS } from 
'@/v2/components/tables/volumesTable';
 import Search from '@/v2/components/search/search';
 
 import { showDataFetchError } from '@/utils/common';
-import { AutoReloadHelper } from '@/utils/autoReloadHelper';
-import { AxiosGetHelper, cancelRequests } from "@/utils/axiosRequestHelper";
 import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
 import { useDebounce } from '@/v2/hooks/useDebounce';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
+import { useAutoReload } from '@/v2/hooks/useAutoReload.hook';
 
 import {
   Volume,
@@ -56,10 +56,12 @@ const SearchableColumnOpts = [
   }
 ]
 
-const Volumes: React.FC<{}> = () => {
-
-  const cancelSignal = useRef<AbortController>();
+const DEFAULT_VOLUMES_RESPONSE: VolumesResponse = {
+  totalCount: 0,
+  volumes: []
+};
 
+const Volumes: React.FC<{}> = () => {
   const defaultColumns = COLUMNS.map(column => ({
     label: column.title as string,
     value: column.key as string,
@@ -70,7 +72,6 @@ const Volumes: React.FC<{}> = () => {
     lastUpdated: 0,
     columnOptions: defaultColumns
   });
-  const [loading, setLoading] = useState<boolean>(false);
   const [currentRow, setCurrentRow] = useState<Volume | Record<string, 
never>>({});
   const [selectedColumns, setSelectedColumns] = 
useState<Option[]>(defaultColumns);
   const [selectedLimit, setSelectedLimit] = useState<Option>(LIMIT_OPTIONS[0]);
@@ -80,65 +81,39 @@ const Volumes: React.FC<{}> = () => {
 
   const debouncedSearch = useDebounce(searchTerm, 300);
 
-  const loadData = () => {
-    setLoading(true);
-    // Cancel any previous pending requests
-    cancelRequests([cancelSignal.current!]);
-
-    const { request, controller } = AxiosGetHelper(
-      '/api/v1/volumes',
-      cancelSignal.current,
-      "",
-      { limit: selectedLimit.value }
-    );
-
-    cancelSignal.current = controller;
-    request.then(response => {
-      const volumesResponse: VolumesResponse = response.data;
-      const volumes: Volume[] = volumesResponse.volumes;
-      const data: Volume[] = volumes?.map(volume => {
-        return {
-          volume: volume.volume,
-          owner: volume.owner,
-          admin: volume.admin,
-          creationTime: volume.creationTime,
-          modificationTime: volume.modificationTime,
-          quotaInBytes: volume.quotaInBytes,
-          quotaInNamespace: volume.quotaInNamespace,
-          usedNamespace: volume.usedNamespace,
-          acls: volume.acls
-        };
-      }) ?? [];
+  // Use the modern hooks pattern
+  const volumesData = useApiData<VolumesResponse>(
+    `/api/v1/volumes?limit=${selectedLimit.value}`,
+    DEFAULT_VOLUMES_RESPONSE,
+    {
+      retryAttempts: 2,
+      initialFetch: false,
+      onError: (error) => showDataFetchError(error)
+    }
+  );
+
+  // Process volumes data when it changes
+  useEffect(() => {
+    if (volumesData.data && volumesData.data.volumes) {
+      const volumes: Volume[] = volumesData.data.volumes.map(volume => ({
+        volume: volume.volume,
+        owner: volume.owner,
+        admin: volume.admin,
+        creationTime: volume.creationTime,
+        modificationTime: volume.modificationTime,
+        quotaInBytes: volume.quotaInBytes,
+        quotaInNamespace: volume.quotaInNamespace,
+        usedNamespace: volume.usedNamespace,
+        acls: volume.acls
+      }));
 
       setState({
         ...state,
-        data,
+        data: volumes,
         lastUpdated: Number(moment()),
       });
-      setLoading(false);
-    }).catch(error => {
-      setLoading(false);
-      showDataFetchError(error.toString());
-    });
-  };
-
-  let autoReloadHelper: AutoReloadHelper = new AutoReloadHelper(loadData);
-
-  useEffect(() => {
-    loadData();
-    autoReloadHelper.startPolling();
-
-    // Component will unmount
-    return (() => {
-      autoReloadHelper.stopPolling();
-      cancelRequests([cancelSignal.current!]);
-    })
-  }, []);
-
-  // If limit changes, load new data
-  useEffect(() => {
-    loadData();
-  }, [selectedLimit.value]);
+    }
+  }, [volumesData.data]);
 
   function handleColumnChange(selected: ValueType<Option, true>) {
     setSelectedColumns(selected as Option[]);
@@ -154,12 +129,18 @@ const Volumes: React.FC<{}> = () => {
     )
   }
 
-
   function handleAclLinkClick(volume: Volume) {
     setCurrentRow(volume);
     setShowPanel(true);
   }
 
+  // Create refresh function for auto-reload
+  const loadVolumesData = () => {
+    volumesData.refetch();
+  };
+
+  const autoReload = useAutoReload(loadVolumesData);
+
   const {
     data, lastUpdated,
     columnOptions
@@ -170,10 +151,10 @@ const Volumes: React.FC<{}> = () => {
       <div className='page-header-v2'>
         Volumes
         <AutoReloadPanel
-          isLoading={loading}
+          isLoading={volumesData.loading}
           lastRefreshed={lastUpdated}
-          togglePolling={autoReloadHelper.handleAutoReloadToggle}
-          onReload={loadData}
+          togglePolling={autoReload.handleAutoReloadToggle}
+          onReload={loadVolumesData}
         />
       </div>
       <div className='data-container'>
@@ -209,7 +190,7 @@ const Volumes: React.FC<{}> = () => {
               }} />
           </div>
           <VolumesTable
-            loading={loading}
+            loading={volumesData.loading}
             data={data}
             handleAclClick={handleAclLinkClick}
             selectedColumns={selectedColumns}
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/buckets/buckets.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/buckets/buckets.tsx
index 64b215b8245..da49975e0e9 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/buckets/buckets.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/buckets/buckets.tsx
@@ -496,7 +496,7 @@ export class Buckets extends React.Component<Record<string, 
object>, IBucketsSta
         loading: false,
         showPanel: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   };
 
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx
index 4a3c11c11b0..7b0e3418a0a 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx
@@ -408,7 +408,7 @@ export class Datanodes extends 
React.Component<Record<string, object>, IDatanode
         loading: false
       });
       decommissionUuids = [];
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     }
     try {
     // Call Datanode API Synchronously after completing Decommission API to 
render Operation Status Column
@@ -450,7 +450,7 @@ export class Datanodes extends 
React.Component<Record<string, object>, IDatanode
         loading: false
       });
       decommissionUuids = [];
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     }
   };
 
@@ -480,7 +480,7 @@ export class Datanodes extends 
React.Component<Record<string, object>, IDatanode
     request.then(() => {
       this._loadData();
     }).catch(error => {
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     }).finally(() => {
       this.setState({
         loading: false,
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/decommissionSummary.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/decommissionSummary.tsx
index 28527c0816e..c4457f50723 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/decommissionSummary.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/decommissionSummary.tsx
@@ -67,7 +67,7 @@ class DecommissionSummary extends 
React.Component<IDecommissionSummaryProps> {
         loading: false,
         summaryData: []
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     }
   };
 
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx
index d154359c94f..1f7bba9aa75 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx
@@ -241,7 +241,7 @@ export class DiskUsage extends 
React.Component<Record<string, object>, IDUState>
       this.setState({
         isLoading: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   };
 
@@ -328,7 +328,7 @@ export class DiskUsage extends 
React.Component<Record<string, object>, IDUState>
               isLoading: false,
               showPanel: false
             });
-            showDataFetchError(error.toString());
+            showDataFetchError(error);
           });
           return;
         }
@@ -480,7 +480,7 @@ export class DiskUsage extends 
React.Component<Record<string, object>, IDUState>
         isLoading: false,
         showPanel: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
 
     const quotaEndpoint = `/api/v1/namespace/quota?path=${path}`;
@@ -525,7 +525,7 @@ export class DiskUsage extends 
React.Component<Record<string, object>, IDUState>
         isLoading: false,
         showPanel: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   }
 
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx
index 63f095ff7ca..59a5b48c992 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx
@@ -17,7 +17,7 @@
  */
 
 import React from 'react';
-import axios, { CanceledError, AxiosError } from 'axios';
+import axios, { CanceledError } from 'axios';
 import filesize from 'filesize';
 import { Row, Col, Tabs, message } from 'antd';
 import { LoadingOutlined } from '@ant-design/icons';
@@ -27,7 +27,7 @@ import { graphic, type EChartsOption } from 'echarts';
 import { EChart } from '@/components/eChart/eChart';
 import { MultiSelect, IOption } from '@/components/multiSelect/multiSelect';
 import { showDataFetchError } from '@/utils/common';
-import { PromiseAllSettledGetHelper, PromiseAllSettledError } from 
'@/utils/axiosRequestHelper';
+import { PromiseAllSettledGetHelper } from '@/utils/axiosRequestHelper';
 
 import './insights.less';
 
@@ -420,7 +420,7 @@ export class Insights extends 
React.Component<Record<string, object>, IInsightsS
       this.setState({
         isLoading: false,
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   }
 
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/om/om.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/om/om.tsx
index fd8d7e2da97..58b460be4c2 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/om/om.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/om/om.tsx
@@ -548,7 +548,7 @@ export class Om extends React.Component<Record<string, 
object>, IOmdbInsightsSta
       this.setState({
         loading: false,
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   };
 
@@ -594,7 +594,7 @@ export class Om extends React.Component<Record<string, 
object>, IOmdbInsightsSta
       this.setState({
         loading: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
 
   };
@@ -653,7 +653,7 @@ export class Om extends React.Component<Record<string, 
object>, IOmdbInsightsSta
       this.setState({
         loading: false,
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   };
 
@@ -730,7 +730,7 @@ export class Om extends React.Component<Record<string, 
object>, IOmdbInsightsSta
       this.setState({
         loading: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   };
 
@@ -764,7 +764,7 @@ export class Om extends React.Component<Record<string, 
object>, IOmdbInsightsSta
       this.setState({
         loading: false,
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   };
 
@@ -835,13 +835,7 @@ export class Om extends React.Component<Record<string, 
object>, IOmdbInsightsSta
             expandedRowData: Object.assign({}, expandedRowData, { 
[record.containerId]: expandedRowState })
           };
         });
-        if (error.name === "CanceledError") {
-          showDataFetchError(cancelRowExpandSignal.signal.reason)
-        }
-        else {
-          console.log(error);
-          showDataFetchError(error.toString());
-        }
+        showDataFetchError(error);
       });
     }
     else {
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/missingContainers/missingContainers.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/missingContainers/missingContainers.tsx
index 9156d68326e..1b0c784fbe6 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/missingContainers/missingContainers.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/missingContainers/missingContainers.tsx
@@ -285,7 +285,7 @@ export class MissingContainers extends 
React.Component<Record<string, object>, I
       this.setState({
         loading: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   }
 
@@ -342,7 +342,7 @@ export class MissingContainers extends 
React.Component<Record<string, object>, I
             expandedRowData: { ...expandedRowData, [record.containerID]: 
expandedRowState }
           };
         });
-        showDataFetchError(error.toString());
+        showDataFetchError(error);
       });
     }
     else {
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx
index 0e8940a928e..817edacdd14 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx
@@ -225,7 +225,7 @@ export class Overview extends 
React.Component<Record<string, object>, IOverviewS
       this.setState({
         loading: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   };
 
@@ -252,7 +252,7 @@ export class Overview extends 
React.Component<Record<string, object>, IOverviewS
       this.setState({
         loading: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   };
 
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/pipelines/pipelines.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/pipelines/pipelines.tsx
index c53be4f7a20..a5001ee0697 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/pipelines/pipelines.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/pipelines/pipelines.tsx
@@ -205,7 +205,7 @@ export class Pipelines extends 
React.Component<Record<string, object>, IPipeline
       this.setState({
         activeLoading: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   };
 
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/volumes/volumes.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/volumes/volumes.tsx
index ad1e94c4d6c..2316a0d8579 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/volumes/volumes.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/volumes/volumes.tsx
@@ -316,7 +316,7 @@ export class Volumes extends React.Component<Record<string, 
object>, IVolumesSta
         loading: false,
         showPanel: false
       });
-      showDataFetchError(error.toString());
+      showDataFetchError(error);
     });
   };
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to