Skip to content

Commit f40dc1d

Browse files
refactor(frontend): finalize abort-safe effect loading flows
Agent-Logs-Url: https://github.com/bg-playground/BGSTM/sessions/9076fdda-8521-48c4-b1fe-54df29bd6181 Co-authored-by: bg-playground <259109604+bg-playground@users.noreply.github.com>
1 parent 26f2481 commit f40dc1d

3 files changed

Lines changed: 37 additions & 9 deletions

File tree

frontend/src/pages/MetricsDashboardPage.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useState } from "react";
1+
import { useCallback, useEffect, useRef, useState } from "react";
22
import { isRequestCanceled } from "../api/client";
33
import traceabilityApi, { type Metrics } from "../api/traceability";
44
import { useToast } from "../context/ToastContext";
@@ -9,6 +9,7 @@ export default function MetricsDashboardPage() {
99
const [metrics, setMetrics] = useState<Metrics | null>(null);
1010
const [loading, setLoading] = useState(true);
1111
const [exporting, setExporting] = useState(false);
12+
const refreshControllerRef = useRef<AbortController | null>(null);
1213
const { showToast } = useToast();
1314

1415
const loadMetrics = useCallback(async (signal?: AbortSignal) => {
@@ -29,6 +30,19 @@ export default function MetricsDashboardPage() {
2930
await loadMetrics(signal);
3031
}, [loadMetrics]);
3132

33+
useEffect(() => {
34+
return () => {
35+
refreshControllerRef.current?.abort();
36+
};
37+
}, []);
38+
39+
const handleRefresh = useCallback(() => {
40+
refreshControllerRef.current?.abort();
41+
const controller = new AbortController();
42+
refreshControllerRef.current = controller;
43+
void loadMetrics(controller.signal);
44+
}, [loadMetrics]);
45+
3246
const handleExportCsv = useCallback(async () => {
3347
try {
3448
setExporting(true);
@@ -88,9 +102,7 @@ export default function MetricsDashboardPage() {
88102
Export CSV
89103
</button>
90104
<button
91-
onClick={() => {
92-
void loadMetrics();
93-
}}
105+
onClick={handleRefresh}
94106
disabled={loading}
95107
className="px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700 disabled:bg-gray-400"
96108
>

frontend/src/pages/TestRunDetailPage.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ const TestRunDetailPage: React.FC = () => {
6060
let total = 0;
6161
do {
6262
if (signal.aborted) break;
63-
const caseRes = await externalResultsApi.listSessionCases(sessionId, { skip, limit }, { signal });
63+
const caseRes = await externalResultsApi.listSessionCases(
64+
sessionId,
65+
{ skip, limit },
66+
{ signal }
67+
);
6468
allCases.push(...caseRes.data.cases);
6569
total = caseRes.data.total;
6670
skip += caseRes.data.cases.length;

frontend/src/pages/TraceabilityMatrixPage.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useState } from "react";
1+
import { useCallback, useEffect, useRef, useState } from "react";
22
import { isRequestCanceled } from "../api/client";
33
import traceabilityApi, { type TraceabilityMatrix } from "../api/traceability";
44
import { useToast } from "../context/ToastContext";
@@ -9,6 +9,7 @@ export default function TraceabilityMatrixPage() {
99
const [matrix, setMatrix] = useState<TraceabilityMatrix | null>(null);
1010
const [loading, setLoading] = useState(true);
1111
const [exporting, setExporting] = useState(false);
12+
const refreshControllerRef = useRef<AbortController | null>(null);
1213
const { showToast } = useToast();
1314

1415
const loadMatrix = useCallback(async (signal?: AbortSignal) => {
@@ -29,6 +30,19 @@ export default function TraceabilityMatrixPage() {
2930
await loadMatrix(signal);
3031
}, [loadMatrix]);
3132

33+
useEffect(() => {
34+
return () => {
35+
refreshControllerRef.current?.abort();
36+
};
37+
}, []);
38+
39+
const handleRefresh = useCallback(() => {
40+
refreshControllerRef.current?.abort();
41+
const controller = new AbortController();
42+
refreshControllerRef.current = controller;
43+
void loadMatrix(controller.signal);
44+
}, [loadMatrix]);
45+
3246
const handleExport = async (format: "csv" | "json" | "pdf") => {
3347
try {
3448
setExporting(true);
@@ -113,9 +127,7 @@ export default function TraceabilityMatrixPage() {
113127
Export PDF
114128
</button>
115129
<button
116-
onClick={() => {
117-
void loadMatrix();
118-
}}
130+
onClick={handleRefresh}
119131
disabled={loading}
120132
className="px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700 disabled:bg-gray-400"
121133
>

0 commit comments

Comments
 (0)