From 7f0a5cd06ae18d8593bbb557f29eb09a1097e252 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Tue, 21 Apr 2026 01:43:20 -0700 Subject: [PATCH] fix(hitl-edit-panel): move duplicate-tag check into functional setTags Fixes #1248 handleAddTag had tags in its useCallback dependency array only so the closure-level duplicate check could read it, which forced the callback to re-create on every tag mutation and compared new additions against a potentially-stale closure value. Collapse the duplicate check into the functional setTags updater so the check always runs against the latest state, and drop tags from the dependency array - the callback is stable for the component's lifetime and downstream memoization won't get invalidated on every keystroke. --- .../hitl-edit-panel/hitl-edit-panel.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx b/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx index bd36431e9..b33392f38 100644 --- a/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx +++ b/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx @@ -65,16 +65,15 @@ function EmailsTagField({ setTags((prev) => (typeof newTags === "function" ? newTags(prev) : newTags)); }, []); - const handleAddTag = useCallback( - (text: string) => { - const trimmed = text.trim(); - if (!trimmed) return; - if (tags.some((tag) => tag.text === trimmed)) return; + const handleAddTag = useCallback((text: string) => { + const trimmed = text.trim(); + if (!trimmed) return; + setTags((prev) => { + if (prev.some((tag) => tag.text === trimmed)) return prev; const newTag: TagType = { id: Date.now().toString(), text: trimmed }; - setTags((prev) => [...prev, newTag]); - }, - [tags] - ); + return [...prev, newTag]; + }); + }, []); return (