From c66d7ecfb23068ec68090029c2be7c5d99eda255 Mon Sep 17 00:00:00 2001 From: Lukas May Date: Thu, 5 Feb 2026 09:05:07 +0100 Subject: [PATCH] fix(21-05): move PlanTasksFetcher onTasks callback to useEffect Replace the setState-during-render pattern in PlanTasksFetcher with a useEffect hook. The onTasks callback was being called directly in the render body when tasksQuery.isSuccess was true, which could cause infinite re-render loops when the parent state update triggered a re-render. Now data flows through useEffect with proper dependencies. --- packages/web/src/routes/initiatives/$id.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/web/src/routes/initiatives/$id.tsx b/packages/web/src/routes/initiatives/$id.tsx index b00a2c5..6a7081c 100644 --- a/packages/web/src/routes/initiatives/$id.tsx +++ b/packages/web/src/routes/initiatives/$id.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback } from "react"; +import { useState, useCallback, useEffect } from "react"; import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { AlertCircle } from "lucide-react"; import { Button } from "@/components/ui/button"; @@ -193,12 +193,13 @@ interface PlanTasksFetcherProps { function PlanTasksFetcher({ planId, onTasks }: PlanTasksFetcherProps) { const tasksQuery = trpc.listTasks.useQuery({ planId }); - // Report tasks upward when loaded - if (tasksQuery.isSuccess && tasksQuery.data) { - // Cast to SerializedTask — tRPC serializes Date to string over JSON - const tasks = tasksQuery.data as unknown as SerializedTask[]; - onTasks(planId, tasks); - } + // Report tasks upward via useEffect (not during render) to avoid + // setState-during-render loops when the parent re-renders on state update. + useEffect(() => { + if (tasksQuery.data) { + onTasks(planId, tasksQuery.data as unknown as SerializedTask[]); + } + }, [tasksQuery.data, planId, onTasks]); return null; // Render nothing — this is a data-fetching component }