refactor: hook dependencies name everything the hooks read

The 12 react-hooks/exhaustive-deps warnings the gate let through:
- ShareDialog and YearFilter built a fresh [] per render while their query loaded, so their
  memos recomputed on every render; they share one empty list now.
- VideoEditor's drag effect called a seek/moveBoundary recreated each render; both are stable
  callbacks in its deps.
- WizardProvider re-checks onboarding on exactly the fields it reads (not the me object, whose
  60 s refetch would reopen a closed wizard).
- Channels, Feed and PlexPlayer list the stable values they already used.
- The player's keydown effect is bound once on purpose (its helpers read refs): said so, with a
  disable, as the codebase's other deliberate sites do.
- dialogFocus and the YouTube player's cleanup read refs React may already have let go of; they
  use what they captured when the effect ran.
This commit is contained in:
2026-09-17 02:45:19 +02:00
parent 5883ea509d
commit 569190480d
10 changed files with 66 additions and 32 deletions
+6 -2
View File
@@ -10,19 +10,23 @@ import { notify } from "../lib/notifications";
import { notifyActionError } from "../lib/actionErrors";
import { copyText } from "../lib/clipboard";
import { formatDate } from "../lib/format";
import { api, type DownloadJob, type ShareLink } from "../lib/api";
import { api, type DownloadJob, type ShareLink, type ShareRecipient } from "../lib/api";
import { inputCls } from "./ui/form";
import { Button } from "./ui/Button";
const EXPIRY_DAYS = [null, 1, 7, 30] as const;
// --- share with a registered user (ACL) -----------------------------------------------------
// One shared empty list while the recipients load: a fresh `[]` per render made `filtered` below
// recompute on every render.
const NO_RECIPIENTS: ShareRecipient[] = [];
function UserShare({ job }: { job: DownloadJob }) {
const { t } = useTranslation();
const [q, setQ] = useState("");
const [open, setOpen] = useState(false);
const recipients = useQuery({ queryKey: qk.shareRecipients(), queryFn: api.shareRecipients });
const list = recipients.data ?? [];
const list = recipients.data ?? NO_RECIPIENTS;
const filtered = useMemo(() => {
const s = q.trim().toLowerCase();
if (!s) return list.slice(0, 8);