-
-
Save tomhicks/95d455186fadda2f7d99e6163aa9d360 to your computer and use it in GitHub Desktop.
function useTaskQueue(params: { | |
shouldProcess: boolean | |
}): { | |
tasks: ReadonlyArray<Task> | |
isProcessing: boolean | |
addTask: (task: Task) => void | |
} { | |
const [queue, setQueue] = React.useState<{ | |
isProcessing: boolean | |
tasks: Array<Task> | |
}>({isProcessing: false, tasks: []}) | |
React.useEffect(() => { | |
if (!params.shouldProcess) return | |
if (queue.tasks.length === 0) return | |
if (queue.isProcessing) return | |
const task = queue.tasks[0] | |
setQueue((prev) => ({ | |
isProcessing: true, | |
tasks: prev.tasks.slice(1), | |
})) | |
Promise.resolve(task()).finally(() => { | |
setQueue((prev) => ({ | |
isProcessing: false, | |
tasks: prev.tasks, | |
})) | |
}) | |
}, [queue, params.shouldProcess]) | |
return { | |
tasks: queue.tasks, | |
isProcessing: queue.isProcessing, | |
addTask: React.useCallback((task) => { | |
setQueue((prev) => ({ | |
isProcessing: prev.isProcessing, | |
tasks: [...prev.tasks, task], | |
})) | |
}, []), | |
} | |
} | |
type Task = () => Promise<void> | void |
Thank you! This was the architecture I was looking for to implement queue processing in the background.
Using this to make image upload to AWS in the non-block blocking way for the end user. Thanks for sharing this!
Thanks ~
I started getting `Maximum update depth exceeded' errors since upgrading to React 18.
After some debugging and research, I could pin the cause on the new automatic batching in React 18.
In this hook, there are two consecutive setQueue(...)
calls inside the useEffect
which must update the internal state in the correct order. Otherwise there's the possibility for inifinite re-renders which cause the above error. This only happens if the enqueued task()
is very fast.
This can be fixed by wrapping the first setQueue
call in flushSync
to prevent batching:
flushSync(() => {
setQueue((prev) => ({
isProcessing: true,
tasks: prev.tasks.slice(1),
}))
})
As the use of flushSync
is discouraged, I'd also be interested in a different solution.
PS. Thanks for the original hook!
Thanks a lot!!