Skip to content

Commit a187168

Browse files
committed
Block auto refresh on filtering
Filter-driven reloads are now centralized to prevent multiple data fetches at once. Further, added a feature to cancel ongoing requests and ensure that older responses don’t replace newer data. After applying filters, there’s a temporary pause on auto-refresh to prevent interruptions while users are still filtering.
1 parent 605f569 commit a187168

3 files changed

Lines changed: 79 additions & 40 deletions

File tree

src/components/shared/TableFilterProfiles.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@ import {
1212
} from "../../thunks/tableThunks";
1313
import { getFilters } from "../../selectors/tableFilterSelectors";
1414
import { FilterData, loadFilterProfile } from "../../slices/tableFilterSlice";
15-
import { AppThunk, useAppDispatch, useAppSelector } from "../../store";
15+
import { useAppDispatch, useAppSelector } from "../../store";
1616
import { useHotkeys } from "react-hotkeys-hook";
1717
import { availableHotkeys } from "../../configs/hotkeysConfig";
18-
import { AsyncThunk } from "@reduxjs/toolkit";
1918
import ButtonLikeAnchor from "./ButtonLikeAnchor";
2019
import { ParseKeys } from "i18next";
2120
import { Resource } from "../../slices/tableSlice";
@@ -29,13 +28,11 @@ const TableFiltersProfiles = ({
2928
showFilterSettings,
3029
setFilterSettings,
3130
loadResource,
32-
loadResourceIntoTable,
3331
resource,
3432
}: {
3533
showFilterSettings: boolean,
3634
setFilterSettings: (_: boolean) => void,
37-
loadResource: AsyncThunk<any, void, any>,
38-
loadResourceIntoTable: () => AppThunk,
35+
loadResource: () => Promise<void>,
3936
resource: Resource,
4037
}) => {
4138
const dispatch = useAppDispatch();
@@ -132,14 +129,13 @@ const TableFiltersProfiles = ({
132129
}
133130
};
134131

135-
const chooseFilterProfile = (filterMap: FilterData[]) => {
132+
const chooseFilterProfile = async (filterMap: FilterData[]) => {
136133
dispatch(loadFilterProfile(filterMap));
137134

138135
// No matter what, we go to page one.
139136
dispatch(goToPage(0));
140137
// Reload resources when filters are removed
141-
dispatch(loadResource());
142-
dispatch(loadResourceIntoTable());
138+
await loadResource();
143139
};
144140

145141
return (

src/components/shared/TableFilters.tsx

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@ import TableFilterProfiles from "./TableFilterProfiles";
1919
import { availableHotkeys } from "../../configs/hotkeysConfig";
2020
import { useHotkeys } from "react-hotkeys-hook";
2121
import moment from "moment";
22-
import { AppThunk, useAppDispatch, useAppSelector } from "../../store";
22+
import { useAppDispatch, useAppSelector } from "../../store";
2323
import { renderValidDate } from "../../utils/dateUtils";
2424
import { getCurrentLanguageInformation } from "../../utils/utils";
2525
import DropDown from "./DropDown";
26-
import { AsyncThunk } from "@reduxjs/toolkit";
2726
import ButtonLikeAnchor from "./ButtonLikeAnchor";
2827
import { ParseKeys } from "i18next";
2928
import SearchContainer from "./SearchContainer";
@@ -36,11 +35,9 @@ import { LuSettings, LuX } from "react-icons/lu";
3635
*/
3736
const TableFilters = ({
3837
loadResource,
39-
loadResourceIntoTable,
4038
resource,
4139
}: {
42-
loadResource: AsyncThunk<any, void, any>,
43-
loadResourceIntoTable: () => AppThunk,
40+
loadResource: () => Promise<void>,
4441
resource: Resource,
4542
}) => {
4643
const { t } = useTranslation();
@@ -77,8 +74,7 @@ const TableFilters = ({
7774
dispatch(resetFilterValues());
7875

7976
// Reload resources when filters are removed
80-
await dispatch(loadResource());
81-
dispatch(loadResourceIntoTable());
77+
await loadResource();
8278
};
8379

8480
// Remove a certain filter
@@ -92,8 +88,7 @@ const TableFilters = ({
9288
dispatch(editFilterValue({ filterName: filter.name, value: "", resource }));
9389

9490
// Reload resources when filter is removed
95-
await dispatch(loadResource());
96-
dispatch(loadResourceIntoTable());
91+
await loadResource();
9792
};
9893

9994
const handleSearchChange = (value: string) => {
@@ -143,8 +138,7 @@ const TableFilters = ({
143138
// No matter what, we go to page one.
144139
dispatch(goToPage(0));
145140
// Reload of resource
146-
await dispatch(loadResource());
147-
dispatch(loadResourceIntoTable());
141+
await loadResource();
148142
};
149143

150144
useEffect(() => {
@@ -208,8 +202,7 @@ const TableFilters = ({
208202
setSelectedFilter("");
209203
// Reload of resource after going to very first page.
210204
dispatch(goToPage(0));
211-
await dispatch(loadResource());
212-
dispatch(loadResourceIntoTable());
205+
await loadResource();
213206
}
214207
}
215208
};
@@ -374,7 +367,6 @@ const TableFilters = ({
374367
setFilterSettings={setFilterSettings}
375368
resource={resource}
376369
loadResource={loadResource}
377-
loadResourceIntoTable={loadResourceIntoTable}
378370
/>
379371
</div>
380372
)}

src/components/shared/TablePage.tsx

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReactNode, useEffect } from "react";
1+
import { ReactNode, useEffect, useRef } from "react";
22
import { useTranslation } from "react-i18next";
33
import TableFilters from "../shared/TableFilters";
44
import Table, { TemplateMap } from "../shared/Table";
@@ -7,7 +7,7 @@ import { fetchFilters } from "../../slices/tableFilterSlice";
77
import { CreateType, NavBarLink } from "../NavBar";
88
import { AppThunk, RootState, useAppDispatch, useAppSelector } from "../../store";
99
import { resetTableProperties, Resource } from "../../slices/tableSlice";
10-
import { AsyncThunk } from "@reduxjs/toolkit";
10+
import { AsyncThunk, PayloadAction } from "@reduxjs/toolkit";
1111
import { ParseKeys } from "i18next";
1212
import { useLocation } from "react-router";
1313
import MainPage from "./MainPage";
@@ -40,37 +40,89 @@ const TablePage = ({
4040
}) => {
4141
const { t } = useTranslation();
4242
const dispatch = useAppDispatch();
43+
const currentLoadRequest = useRef<{ abort:() => void } | null>(null);
44+
const latestLoadRequestId = useRef(0);
45+
const allowLoadIntoTable = useRef(true);
46+
const currentLoadSource = useRef<"auto" | "filters">(null);
47+
const autoRefreshPaused = useRef(false);
48+
const autoRefreshPauseTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
4349

4450
const location = useLocation();
4551

4652
const numberOfRows = useAppSelector(state => getTotalResources(state));
4753

54+
const pauseAutoRefresh = () => {
55+
autoRefreshPaused.current = true;
56+
57+
if (autoRefreshPauseTimeout.current) {
58+
clearTimeout(autoRefreshPauseTimeout.current);
59+
}
60+
61+
autoRefreshPauseTimeout.current = setTimeout(() => {
62+
autoRefreshPaused.current = false;
63+
autoRefreshPauseTimeout.current = null;
64+
}, 2000);
65+
};
66+
67+
const loadResource = async (source: "auto" | "filters" = "auto") => {
68+
if (source === "filters") {
69+
pauseAutoRefresh();
70+
}
71+
72+
if (source === "auto" && autoRefreshPaused.current) {
73+
return;
74+
}
75+
76+
if (source === "auto" && currentLoadSource.current === "filters") {
77+
return;
78+
}
79+
80+
const requestId = ++latestLoadRequestId.current;
81+
currentLoadSource.current = source;
82+
83+
currentLoadRequest.current?.abort?.();
84+
85+
const fetchRequest = dispatch(fetchResource()) as Promise<PayloadAction<any, string>> & { abort:() => void };
86+
currentLoadRequest.current = fetchRequest;
87+
88+
const fetchResult = await fetchRequest as { meta?: { requestStatus?: string } };
89+
90+
if (requestId === latestLoadRequestId.current) {
91+
currentLoadSource.current = null;
92+
}
93+
94+
if (
95+
allowLoadIntoTable.current
96+
&& requestId === latestLoadRequestId.current
97+
&& fetchResult?.meta?.requestStatus === "fulfilled"
98+
) {
99+
dispatch(loadResourceIntoTable());
100+
}
101+
};
102+
103+
const loadResourceFromFilters = () => loadResource("filters");
104+
48105
useEffect(() => {
49-
// State variable for interrupting the load function
50-
let allowLoadIntoTable = true;
106+
allowLoadIntoTable.current = true;
51107

52108
// Clear table of previous data
53109
dispatch(resetTableProperties());
54110

55111
dispatch(fetchFilters(resource));
56112

57113
// Load resource on mount
58-
const loadResource = async () => {
59-
// Fetching resources from server
60-
await dispatch(fetchResource());
61-
62-
// Load resources into table
63-
if (allowLoadIntoTable) {
64-
dispatch(loadResourceIntoTable());
65-
}
66-
};
67-
loadResource();
114+
loadResource("auto");
68115

69116
// Fetch resources every minute
70-
const fetchResourceInterval = setInterval(loadResource, 5000);
117+
const fetchResourceInterval = setInterval(() => loadResource("auto"), 5000);
71118

72119
return () => {
73-
allowLoadIntoTable = false;
120+
allowLoadIntoTable.current = false;
121+
currentLoadRequest.current?.abort?.();
122+
if (autoRefreshPauseTimeout.current) {
123+
clearTimeout(autoRefreshPauseTimeout.current);
124+
autoRefreshPauseTimeout.current = null;
125+
}
74126
clearInterval(fetchResourceInterval);
75127
};
76128
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -91,8 +143,7 @@ const TablePage = ({
91143

92144
{/* Include filters component */}
93145
<TableFilters
94-
loadResource={fetchResource}
95-
loadResourceIntoTable={loadResourceIntoTable}
146+
loadResource={loadResourceFromFilters}
96147
resource={resource}
97148
/>
98149
</div>

0 commit comments

Comments
 (0)