From 5c5d51350fb23983a4768c67ba45219dd792527c Mon Sep 17 00:00:00 2001 From: nessi Date: Fri, 13 Feb 2026 10:06:56 +0100 Subject: [PATCH] Fix stale `refresh` usage in QueryInsightsPage effect hooks Replaced `refresh` with `useRef` to ensure the latest value is always used in async operations. Added cleanup logic to handle component unmounts during API calls, preventing state updates on unmounted components. --- frontend/src/pages/QueryInsightsPage.jsx | 26 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/QueryInsightsPage.jsx b/frontend/src/pages/QueryInsightsPage.jsx index ac2c04b..76ebe46 100644 --- a/frontend/src/pages/QueryInsightsPage.jsx +++ b/frontend/src/pages/QueryInsightsPage.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { apiFetch } from "../api"; import { useAuth } from "../state"; @@ -62,6 +62,7 @@ function buildQueryTips(row) { export function QueryInsightsPage() { const { tokens, refresh } = useAuth(); + const refreshRef = useRef(refresh); const [targets, setTargets] = useState([]); const [targetId, setTargetId] = useState(""); const [rows, setRows] = useState([]); @@ -71,6 +72,10 @@ export function QueryInsightsPage() { const [error, setError] = useState(""); const [loading, setLoading] = useState(true); + useEffect(() => { + refreshRef.current = refresh; + }, [refresh]); + useEffect(() => { (async () => { try { @@ -89,17 +94,26 @@ export function QueryInsightsPage() { useEffect(() => { if (!targetId) return; + let active = true; (async () => { try { - const data = await apiFetch(`/targets/${targetId}/top-queries`, {}, tokens, refresh); + const data = await apiFetch(`/targets/${targetId}/top-queries`, {}, tokens, refreshRef.current); + if (!active) return; setRows(data); - setSelectedQuery(data[0] || null); - setPage(1); + setSelectedQuery((prev) => { + if (!prev) return data[0] || null; + const keep = data.find((row) => row.queryid === prev.queryid); + return keep || data[0] || null; + }); + setPage((prev) => (prev === 1 ? prev : 1)); } catch (e) { - setError(String(e.message || e)); + if (active) setError(String(e.message || e)); } })(); - }, [targetId, tokens, refresh]); + return () => { + active = false; + }; + }, [targetId, tokens?.accessToken, tokens?.refreshToken]); const dedupedByQueryId = [...rows].reduce((acc, row) => { if (!row?.queryid) return acc;