diff --git a/web/src/features/metrics/pages/MetricsPage.tsx b/web/src/features/metrics/pages/MetricsPage.tsx index c157e78e9..8721acf4f 100644 --- a/web/src/features/metrics/pages/MetricsPage.tsx +++ b/web/src/features/metrics/pages/MetricsPage.tsx @@ -951,7 +951,6 @@ const QueryBrowserWrapper: FC<{ const { plugin } = useMonitoring(); const [activeNamespace] = useActiveNamespace(); const [queryParams, setQueryParams] = useSearchParams(); - const [isFirstRender, , , setFirstRenderFalse] = useBoolean(true); const dispatch = useDispatch(); @@ -962,26 +961,6 @@ const QueryBrowserWrapper: FC<{ (state: MonitoringState) => getObserveState(plugin, state).queryBrowser?.queries, ); - // Initialize queries from URL parameters on first render - useEffect(() => { - if (!isFirstRender) { - return; - } - dispatch(queryBrowserDeleteAllQueries()); - for (let i = 0; queryParams.has(`query${i}`); i++) { - const query = queryParams.get(`query${i}`); - dispatch( - queryBrowserPatchQuery(i, { - isEnabled: true, - isExpanded: true, - query, - text: query, - }), - ); - } - setFirstRenderFalse(); - }, [dispatch, queryParams, isFirstRender, setFirstRenderFalse]); - /* eslint-disable react-hooks/exhaustive-deps */ // Use useMemo() to prevent these two arrays being recreated on every render, which would // trigger unnecessary re-renders of QueryBrowser, which can be quite slow @@ -995,21 +974,68 @@ const QueryBrowserWrapper: FC<{ [disabledSeriesMemoKey], ); - // Update the URL parameters when the queries shown in the graph change + const prevParams = useRef(null); + const prevQueryStrings = useRef(null); + + // Keep URL query0/query1/… and Redux in sync. useEffect(() => { - if (isFirstRender) { + const fromUrl: string[] = []; + for (let i = 0; queryParams.has(`query${i}`); i++) { + fromUrl.push(queryParams.get(`query${i}`) ?? ''); + } + + const first = prevParams.current === null; + const urlChanged = first || prevParams.current !== queryParams; + const storeChanged = first || prevQueryStrings.current !== queryStrings; + prevParams.current = queryParams; + prevQueryStrings.current = queryStrings; + + if (JSON.stringify(fromUrl) === JSON.stringify(queryStrings)) { return; } - const newParams = new URLSearchParams(removeQueryKeys(queryParams)); - queryStrings.forEach((query, i) => newParams.set(`query${i}`, query || '')); - if (customDataSourceName) { - newParams.set(QueryParams.Datasource, customDataSourceName); + + const writeUrlFromStore = () => { + const newParams = new URLSearchParams(removeQueryKeys(queryParams)); + queryStrings.forEach((query, i) => newParams.set(`query${i}`, query || '')); + if (customDataSourceName) { + newParams.set(QueryParams.Datasource, customDataSourceName); + } + if (!newParams.get(QueryParams.Units)) { + newParams.set(QueryParams.Units, 'short'); + } + setQueryParams(newParams, { replace: true }); + }; + + const writeStoreFromUrl = () => { + dispatch(queryBrowserDeleteAllQueries()); + fromUrl.forEach((query, i) => { + dispatch( + queryBrowserPatchQuery(i, { + isEnabled: true, + isExpanded: true, + query, + text: query, + }), + ); + }); + }; + + if (first) { + if (fromUrl.some((q) => q)) { + writeStoreFromUrl(); + } else { + writeUrlFromStore(); + } + return; + } + if (urlChanged) { + writeStoreFromUrl(); + return; } - if (!newParams.get(QueryParams.Units)) { - newParams.set(QueryParams.Units, 'short'); + if (storeChanged) { + writeUrlFromStore(); } - setQueryParams(newParams, { replace: true }); - }, [queryStrings, customDataSourceName, isFirstRender, queryParams]); + }, [customDataSourceName, dispatch, queryParams, queryStrings, setQueryParams]); if (hideGraphs) { return null;