From b9859e96e9c8c9096e57bd5062dcb88500ca1e31 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 6 Dec 2024 22:36:43 -0500 Subject: [PATCH] fix: ensures autosaves run sequentially rather than ever in parallel --- packages/ui/src/elements/Autosave/index.tsx | 37 ++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/elements/Autosave/index.tsx b/packages/ui/src/elements/Autosave/index.tsx index 38ec81d20dc..1e10c321265 100644 --- a/packages/ui/src/elements/Autosave/index.tsx +++ b/packages/ui/src/elements/Autosave/index.tsx @@ -3,7 +3,7 @@ import type { ClientCollectionConfig, ClientGlobalConfig } from 'payload' import { versionDefaults } from 'payload/shared' -import React, { useEffect, useRef, useState } from 'react' +import React, { useCallback, useEffect, useRef, useState } from 'react' import { toast } from 'sonner' import { @@ -64,6 +64,9 @@ export const Autosave: React.FC = ({ id, collection, global: globalDoc }) interval = versionsConfig.drafts.autosave.interval } + const [queue, setQueue] = useState<(() => Promise)[]>([]) + const [isProcessing, setIsProcessing] = useState(false) + const [saving, setSaving] = useState(false) const debouncedFields = useDebounce(fields, interval) const modified = useDebounce(formModified, interval) @@ -88,6 +91,32 @@ export const Autosave: React.FC = ({ id, collection, global: globalDoc }) // can always retrieve the most to date locale localeRef.current = locale + // Function to process the queue + const processQueue = useCallback(async () => { + if (isProcessing || queue.length === 0) { + return + } + + setIsProcessing(true) + const currentTask = queue[0] + + try { + await currentTask() + } catch (error) { + console.error('Error autosaving:', error) + } finally { + setQueue((prevQueue) => prevQueue.slice(1)) + setIsProcessing(false) + } + }, [isProcessing, queue]) + + // Process the queue whenever it changes + useEffect(() => { + if (!isProcessing) { + void processQueue() + } + }, [queue, isProcessing, processQueue]) + // When debounced fields change, autosave useIgnoredEffect( () => { @@ -97,7 +126,7 @@ export const Autosave: React.FC = ({ id, collection, global: globalDoc }) let startTimestamp = undefined let endTimestamp = undefined - const autosave = () => { + const autosave = async () => { if (modified) { startTimestamp = new Date().getTime() @@ -129,7 +158,7 @@ export const Autosave: React.FC = ({ id, collection, global: globalDoc }) submitted && !valid && versionsConfig?.drafts && versionsConfig?.drafts?.validate if (!skipSubmission) { - void fetch(url, { + await fetch(url, { body: JSON.stringify(data), credentials: 'include', headers: { @@ -229,7 +258,7 @@ export const Autosave: React.FC = ({ id, collection, global: globalDoc }) } } - void autosave() + setQueue((prevQueue) => [...prevQueue, autosave]) return () => { if (autosaveTimeout) {