From d8a10fadc0e3456803145377de5ec21ac0734467 Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Thu, 12 Mar 2026 00:23:15 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20React=20error=20#310=20=E2=80=94=20useMe?= =?UTF-8?q?mo=20after=20early=20return=20in=20detail=20panel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move threadChain useMemo before the `if (!entry) return null` early return to keep hook call order stable between renders. When entry was null, the hook was skipped, causing "Rendered more hooks than during the previous render" crash on subsequent renders with entry set. Co-Authored-By: Claude Opus 4.6 --- .../components/registry-entry-detail.tsx | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/modules/registratura/components/registry-entry-detail.tsx b/src/modules/registratura/components/registry-entry-detail.tsx index 3982328..2233c27 100644 --- a/src/modules/registratura/components/registry-entry-detail.tsx +++ b/src/modules/registratura/components/registry-entry-detail.tsx @@ -177,26 +177,16 @@ export function RegistryEntryDetail({ setTimeout(() => setCopiedPath(null), 2000); }, []); - if (!entry) return null; - - const dir = DIRECTION_CONFIG[entry.direction] ?? DIRECTION_CONFIG.intrat; - const DirIcon = dir.icon; - const status = STATUS_CONFIG[entry.status] ?? STATUS_CONFIG.deschis; - - const overdueDays = - entry.status === "deschis" ? getOverdueDays(entry.deadline) : null; - const isOverdue = overdueDays !== null && overdueDays > 0; - - const threadParent = entry.threadParentId - ? allEntries.find((e) => e.id === entry.threadParentId) - : null; - const threadChildren = allEntries.filter( - (e) => e.threadParentId === entry.id, - ); - - // Build full chain for mini flow diagram + // Build full chain for mini flow diagram (must be before early return to keep hook order stable) const threadChain = useMemo(() => { - if (!threadParent && threadChildren.length === 0) return []; + if (!entry) return []; + const threadParentEntry = entry.threadParentId + ? allEntries.find((e) => e.id === entry.threadParentId) + : null; + const threadChildEntries = allEntries.filter( + (e) => e.threadParentId === entry.id, + ); + if (!threadParentEntry && threadChildEntries.length === 0) return []; const byId = new Map(allEntries.map((e) => [e.id, e])); // Walk up to root let root = entry; @@ -222,7 +212,24 @@ export function RegistryEntryDetail({ } chain.sort((a, b) => a.date.localeCompare(b.date)); return chain; - }, [entry, threadParent, threadChildren, allEntries]); + }, [entry, allEntries]); + + if (!entry) return null; + + const dir = DIRECTION_CONFIG[entry.direction] ?? DIRECTION_CONFIG.intrat; + const DirIcon = dir.icon; + const status = STATUS_CONFIG[entry.status] ?? STATUS_CONFIG.deschis; + + const overdueDays = + entry.status === "deschis" ? getOverdueDays(entry.deadline) : null; + const isOverdue = overdueDays !== null && overdueDays > 0; + + const threadParent = entry.threadParentId + ? allEntries.find((e) => e.id === entry.threadParentId) + : null; + const threadChildren = allEntries.filter( + (e) => e.threadParentId === entry.id, + ); return (