fix(ui): stabilize issue search URL sync and debounce query execution
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useDeferredValue, useMemo, useState, useCallback, useRef } from "react";
|
import { useEffect, useMemo, useState, useCallback, useRef } from "react";
|
||||||
import { Link } from "@/lib/router";
|
import { Link } from "@/lib/router";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useDialog } from "../context/DialogContext";
|
import { useDialog } from "../context/DialogContext";
|
||||||
@@ -175,8 +175,19 @@ export function IssuesList({
|
|||||||
const [assigneePickerIssueId, setAssigneePickerIssueId] = useState<string | null>(null);
|
const [assigneePickerIssueId, setAssigneePickerIssueId] = useState<string | null>(null);
|
||||||
const [assigneeSearch, setAssigneeSearch] = useState("");
|
const [assigneeSearch, setAssigneeSearch] = useState("");
|
||||||
const [issueSearch, setIssueSearch] = useState(initialSearch ?? "");
|
const [issueSearch, setIssueSearch] = useState(initialSearch ?? "");
|
||||||
const deferredIssueSearch = useDeferredValue(issueSearch);
|
const [debouncedIssueSearch, setDebouncedIssueSearch] = useState(issueSearch);
|
||||||
const normalizedIssueSearch = deferredIssueSearch.trim();
|
const normalizedIssueSearch = debouncedIssueSearch.trim();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIssueSearch(initialSearch ?? "");
|
||||||
|
}, [initialSearch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timeoutId = window.setTimeout(() => {
|
||||||
|
setDebouncedIssueSearch(issueSearch);
|
||||||
|
}, 300);
|
||||||
|
return () => window.clearTimeout(timeoutId);
|
||||||
|
}, [issueSearch]);
|
||||||
|
|
||||||
// Reload view state from localStorage when company changes (scopedKey changes).
|
// Reload view state from localStorage when company changes (scopedKey changes).
|
||||||
const prevScopedKey = useRef(scopedKey);
|
const prevScopedKey = useRef(scopedKey);
|
||||||
|
|||||||
@@ -22,13 +22,19 @@ export function Issues() {
|
|||||||
const handleSearchChange = useCallback((search: string) => {
|
const handleSearchChange = useCallback((search: string) => {
|
||||||
clearTimeout(debounceRef.current);
|
clearTimeout(debounceRef.current);
|
||||||
debounceRef.current = setTimeout(() => {
|
debounceRef.current = setTimeout(() => {
|
||||||
|
const trimmedSearch = search.trim();
|
||||||
|
const currentSearch = new URLSearchParams(window.location.search).get("q") ?? "";
|
||||||
|
if (currentSearch === trimmedSearch) return;
|
||||||
|
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
if (search.trim()) {
|
if (trimmedSearch) {
|
||||||
url.searchParams.set("q", search.trim());
|
url.searchParams.set("q", trimmedSearch);
|
||||||
} else {
|
} else {
|
||||||
url.searchParams.delete("q");
|
url.searchParams.delete("q");
|
||||||
}
|
}
|
||||||
window.history.replaceState(null, "", url);
|
|
||||||
|
const nextUrl = `${url.pathname}${url.search}${url.hash}`;
|
||||||
|
window.history.replaceState(window.history.state, "", nextUrl);
|
||||||
}, 300);
|
}, 300);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user