11'use client'
22
3- import { useState , useEffect , useCallback } from 'react'
3+ import { useState , useEffect , useCallback , useRef } from 'react'
44import { Task } from '@/lib/db/schema'
55
66export function useTask ( taskId : string ) {
77 const [ task , setTask ] = useState < Task | null > ( null )
88 const [ isLoading , setIsLoading ] = useState ( true )
99 const [ error , setError ] = useState < string | null > ( null )
10+ const attemptCountRef = useRef ( 0 )
11+ const hasFoundTaskRef = useRef ( false )
1012
1113 const fetchTask = useCallback ( async ( ) => {
1214 try {
@@ -15,33 +17,65 @@ export function useTask(taskId: string) {
1517 const data = await response . json ( )
1618 setTask ( data . task )
1719 setError ( null )
20+ hasFoundTaskRef . current = true
1821 } else if ( response . status === 404 ) {
19- setError ( 'Task not found' )
20- setTask ( null )
22+ // Only set error after multiple failed attempts (to handle race condition on task creation)
23+ // Wait for at least 3 attempts (up to ~6 seconds) before showing "Task not found"
24+ attemptCountRef . current += 1
25+ if ( attemptCountRef . current >= 3 || hasFoundTaskRef . current ) {
26+ setError ( 'Task not found' )
27+ setTask ( null )
28+ }
29+ // If we haven't hit the attempt threshold yet, keep loading state
2130 } else {
2231 setError ( 'Failed to fetch task' )
2332 }
2433 } catch ( err ) {
2534 console . error ( 'Error fetching task:' , err )
2635 setError ( 'Failed to fetch task' )
2736 } finally {
28- setIsLoading ( false )
37+ // Only stop loading after we've either found the task or exceeded attempt threshold
38+ if ( hasFoundTaskRef . current || attemptCountRef . current >= 3 || error ) {
39+ setIsLoading ( false )
40+ }
2941 }
30- } , [ taskId ] )
42+ } , [ taskId , error ] )
3143
32- // Initial fetch
44+ // Initial fetch with retry logic
3345 useEffect ( ( ) => {
46+ attemptCountRef . current = 0
47+ hasFoundTaskRef . current = false
48+ setIsLoading ( true )
49+ setError ( null )
50+
51+ // Fetch immediately
3452 fetchTask ( )
35- } , [ fetchTask ] )
3653
37- // Poll for updates every 5 seconds
54+ // If task isn't found on first try, retry more aggressively initially
55+ // This handles the race condition where we navigate to the task page before the DB insert completes
56+ const retryInterval = setInterval ( ( ) => {
57+ if ( ! hasFoundTaskRef . current && attemptCountRef . current < 3 ) {
58+ fetchTask ( )
59+ } else {
60+ clearInterval ( retryInterval )
61+ }
62+ } , 2000 ) // Check every 2 seconds for the first few attempts
63+
64+ return ( ) => clearInterval ( retryInterval )
65+ // eslint-disable-next-line react-hooks/exhaustive-deps
66+ } , [ taskId ] ) // fetchTask is intentionally not in deps to avoid recreating interval on every fetchTask change
67+
68+ // Poll for updates every 5 seconds after initial load
3869 useEffect ( ( ) => {
39- const interval = setInterval ( ( ) => {
40- fetchTask ( )
41- } , 5000 )
70+ // Only start polling after we've found the task or given up
71+ if ( ! isLoading ) {
72+ const interval = setInterval ( ( ) => {
73+ fetchTask ( )
74+ } , 5000 )
4275
43- return ( ) => clearInterval ( interval )
44- } , [ fetchTask ] )
76+ return ( ) => clearInterval ( interval )
77+ }
78+ } , [ fetchTask , isLoading ] )
4579
4680 return { task, isLoading, error, refetch : fetchTask }
4781}
0 commit comments