Skip to content

Chapter 11: Spamming 'Create Invoice' or 'Edit Invoice' multiple times leads to duplicate creates/edits #1326

Description

@alui07

Before submitting

  • I have searched existing issues to make sure this isn't a duplicate
  • I have completed this chapter and encountered the issue myself

Chapter URL

https://nextjs.org/learn/dashboard-app/mutating-data

Issue Type

Other

Description

After implementing both the create and edit invoice sections (Chapter 11), I decided to just kind of mess around and test the current state of my app. I've followed the chapters to the letter and everything is functioning fine.

Issue: When I go to create an invoice, after filling out the information, I can spam click the "Create Invoice" submit button multiple times sending equal number of POST requests to the server/database resulting in duplicate entries in the database BEFORE the router routes me back to /dashboard/invoices.

I get this is just a tutorial, so it's not that serious. A simple fix would be to just disable the button after the first click. It's not so crazy with Edit invoice, but I can still see the multiple SQL queries, so having a class that can be utilized for both create & edit would be nice.

I've "talked" to my good friend Claude Sonnet and this is what she came up with:

'use client';

import { useFormStatus } from 'react-dom';
import { Button } from '@/app/ui/button';

export function SubmitButton({ children }: { children: React.ReactNode }) {
  const { pending } = useFormStatus();

  return (
    <Button type="submit" disabled={pending}>
      {pending ? 'Submitting...' : children}
    </Button>
  );
}

adding this to the /app/ui folder or we could modify /app/ui/button.tsx and transform it into a client component:

'use client';

import clsx from 'clsx';
import { useFormStatus } from 'react-dom';

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  children: React.ReactNode;
}

export function Button({ children, className, ...rest }: ButtonProps) {
  const { pending } = useFormStatus();

  return (
    <button
      {...rest}
      className={clsx(
        'flex h-10 items-center rounded-lg bg-blue-500 px-4 text-sm font-medium text-white transition-colors hover:bg-blue-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 active:bg-blue-600 aria-disabled:cursor-not-allowed aria-disabled:opacity-50',
        className,
      )}
      disabled={pending}
    >
      {pending ? 'Submitting...': children}
    </button>
  );
}

Expected vs Actual Behavior

Expected: Only 1 invoice to be created with 3 button clicks of "Create Invoice".
Actual: Three duplicate invoices were created. Checkout the screenshot.

Environment (optional)

No response

Screenshots (optional)

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions