Demo data: three made-up repos and three people, recorded by the real Trail hooks.About TrailGitHub
78 lines · 2.5 KBRaw

claude session d02659b5-ec03-4d34-b363-1f0767df0d19

User

On slow 3G the checkout button flickers and some people manage to place the order twice. Fix it.

Tool call: Read

{
 "file_path": "/home/dev/code/web-app/src/components/CheckoutButton.tsx"
}

Tool result

     1	export function CheckoutButton({ cart }: { cart: Cart }) {
     2	  const [loading, setLoading] = useState(false);
     3	
     4	  async function submit() {
     5	    setLoading(true);
     6	    await placeOrder(cart);
     7	    setLoading(false);
     8	  }
     9	
    10	  return <button onClick={submit}>{loading ? <Spinner /> : "Place order"}</button>;
    11	}

Assistant (thinking)

The button is never disabled, so a second tap during the request fires submit again. The flicker is the label swapping to a spinner, which changes the button width. Disable while pending, guard with a ref (state updates are async, two fast taps both see loading=false), and keep the width fixed.

Tool call: Edit

{
 "file_path": "/home/dev/code/web-app/src/components/CheckoutButton.tsx",
 "old_string": "  const [loading, setLoading] = useState(false);\n\n  async function submit() {\n    setLoading(true);\n    await placeOrder(cart);\n    setLoading(false);\n  }\n\n  return <button onClick={submit}>{loading ? <Spinner /> : \"Place order\"}</button>;\n",
 "new_string": "  const [loading, setLoading] = useState(false);\n  const inFlight = useRef(false); // two quick taps both see loading=false\n\n  async function submit() {\n    if (inFlight.current) return;\n    inFlight.current = true;\n    setLoading(true);\n    try {\n      await placeOrder(cart);\n    } finally {\n      inFlight.current = false;\n      setLoading(false);\n    }\n  }\n\n  return (\n    <button onClick={submit} disabled={loading} aria-busy={loading} style={{ minWidth: 140 }}>\n      {loading ? <Spinner /> : \"Place order\"}\n    </button>\n  );\n"
}

Tool result

The file src/components/CheckoutButton.tsx has been updated.

Tool call: Bash

{
 "command": "npx tsc --noEmit",
 "description": "Type check"
}

Tool result

Assistant

The button is disabled while the order is pending, a ref blocks a second tap that lands before the re-render, and a fixed min-width stops the flicker when the label swaps to the spinner.

Tool call: Bash

{
 "command": "git commit -am \"Disable checkout button while the order is pending\"",
 "description": "Commit"
}