This update resolves a critical race condition between the useAutosave hook and the manual handleSubmit function in NoteForm.tsx. Previously, these two mechanisms operated independently, leading to duplicate database entries (double POST requests) and redundant attachment records when a user saved a note while an autosave timer was pending.

The Problem: Race Condition

The core issue stems from two parallel "save" paths executing simultaneously. When a user types and immediately clicks "Save," the manual submission fires a POST request, but fails to cancel the pending autosave timer. When the timer expires moments later, it fires a second request.

Root Cause Analysis

  1. Unchecked Debounce: handleSubmit did not cancel the active 1-second debounce timer from useAutosave.
  2. Method Mismatch (Add Mode): In "Add Mode," if autosave had already created the note (via PUT upsert), handleSubmit ignored this state and forced a POST request, creating a second, duplicate note ID.
  3. Duplicate Attachments: Because handleSubmit assumed it was always creating a fresh note, it attempted to re-create attachment links that were already handled during the upload/autosave flow.

Implementation Details

1. File: src/hooks/useAutosave.ts

Goal: Expose a mechanism to abort pending saves.

We added a cancel function to the hook's return interface. This allows consuming components to stop the debouncedSave timer without triggering the side effect.

TypeScript

// Added to return interface
export interface AutosaveReturn {
    // ... existing types
    cancel: () => void;
}

// Implementation
const cancel = useCallback(() => {
    debouncedSave.current.cancel();
}, []);

2. File: src/components/NoteForm.tsx

Goal: Coordinate the save logic and handle "Add vs. Update" states intelligently.

A. Cancel Race Condition
At the very start of handleSubmit, we now strictly cancel any pending autosave to ensure only one network request occurs.

TypeScript

const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    autosave.cancel(); // <--- NEW: Stops the race condition
    // ...

B. Conditional Method Selection
We replaced the blind POST logic with a conditional check. If noteCreated is true (meaning autosave already established the record), we switch to PUT.

TypeScript

// Old Logic: Always POST in add mode
// New Logic:
if (noteCreated) {
    // Note exists via autosave -> Update (PUT)
    response = await fetch(`${API_BASE_URL}/notes?noteId=${noteId}...`, {
        method: 'PUT',
        // ...
    });
} else {
    // Fresh submission -> Create (POST)
    response = await fetch(`${API_BASE_URL}/notes?personId=${personId}...`, {
        method: 'POST',
        // ...
    });
}

C. Prevent Attachment Duplication
We added a guard clause to the attachment creation loop. If the note was already created via autosave, attachments are already linked; we skip re-linking them to prevent database errors.

TypeScript

// Only link attachments if this is a fresh POST
if (!isEditMode && !noteCreated && data?.id) {
    // create attachment links...
}

Verification & Testing

To ensure the fix is robust, please perform the following validation steps:

ScenarioActionExpected Outcome
Quick SaveType "Test" and click Save immediately (<1s).Single note created. No duplicate API calls in Network tab.
Slow SaveType "Test", wait for autosave indicator, then click Save.Single note created. handleSubmit uses PUT instead of POST.
UploadsUpload an image, then click Save.Single note with exactly one set of attachments.
CancellationType "Test", then click the "X" (Close) button.Autosave flushes, modal closes. Single note created.

Choice B

System Update: Resolving Duplicate Note Creations

We are rolling out an architectural fix to address an issue where creating or editing a note sometimes resulted in duplicate entries or redundant server requests.

The Core Issue

The application utilizes two distinct saving mechanisms: a background Autosave (triggered automatically after a brief pause in typing) and a Manual Save (triggered by clicking the Save button). Previously, these two systems operated completely independently without coordinating.

If a user typed content and quickly clicked "Save," both mechanisms would fire simultaneously. When creating a new note, the Autosave would generate a draft note on the server in the background, and the Manual Save would immediately force the creation of a second, identical note. This lack of synchronization also led to duplicated image attachment records in the backend database.

How We Fixed It

We restructured the logic within the note creation form and the autosave hook to ensure they communicate effectively.

  • Interrupting the Autosave: We introduced a new cancellation function to the background process. Now, when a user clicks the "Save" button, the system instantly cancels any pending autosave timers. This eliminates the race condition that caused simultaneous server requests.
  • Intelligent Draft Handling: The save logic now checks if the Autosave system has already generated a draft in the background. If a draft exists, the manual save will issue an update command (PUT) to modify that exact note. It will only issue a create command (POST) if no background draft was initiated.
  • Preventing Duplicate Attachments: We refined the attachment processing loop. The system now recognizes when images have already been linked during the background upload phase, bypassing the redundant attachment step upon final save.
  • ID Synchronization: The application now strictly enforces the use of the canonical, pre-generated note ID across all states, ensuring all content reliably syncs to the correct record.

Expected Behavior Post-Update

  • Quick Saves: Typing and immediately clicking save will yield exactly one note in the final list.
  • Media Uploads: Uploading images followed by a manual save will result in a single note with one correct set of attachments.
  • Passive Saves: Closing a note window instead of clicking save will cleanly complete the final autosave process, leaving one accurate draft.

Would you like me to convert these expected behaviors into a step-by-step Quality Assurance testing script for your team?