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.
This commit is contained in:
Lukas May
2026-02-05 09:05:07 +01:00
parent 1530d7ab15
commit c66d7ecfb2

View File

@@ -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
}