fix: call logoutAction directly via useTransition instead of form-ref submit
De form-ref-dance werkte niet betrouwbaar in de huidige base-ui:
- onSelect vuurde requestSubmit() op een hidden form
- Form zat eerst binnen DropdownMenuContent (form geunmount → ref null)
- Form daarna naar top-level verplaatst — vuurde nog steeds geen request af,
vermoedelijk doordat onSelect in deze base-ui-build niet (consistent) een
click-event genereerde dat de form-API trigger'de
Vervang door directe call: Server Actions kunnen sinds Next.js 14 als async
functie worden aangeroepen vanuit Client Components. useTransition voorkomt
dat de UI bevriest tijdens de redirect.
Naast onSelect ook onClick als veiligheid voor het geval base-ui later weer
van event-prop wisselt — beide handlers wijzen naar dezelfde idempotente
function (handleLogout via startTransition).
Pendingstate ('Uitloggen…' label, disabled item) zodat dubbele klikken niet
dubbele logoutAction-calls afvuren.
Quality gates: lint 0 errors, tsc clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4f9a6d2d9e
commit
5cbf543c16
1 changed files with 17 additions and 11 deletions
|
|
@ -1,6 +1,6 @@
|
|||
'use client'
|
||||
|
||||
import { useRef } from 'react'
|
||||
import { useTransition } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { Settings, Sun, Globe, LogOut } from 'lucide-react'
|
||||
import { logoutAction } from '@/actions/auth'
|
||||
|
|
@ -33,14 +33,19 @@ export function UserMenu({ userId, username, email, roles }: UserMenuProps) {
|
|||
const initials = username.slice(0, 2).toUpperCase()
|
||||
const roleLabels = roles.map((r) => ROLE_LABELS[r]).filter(Boolean)
|
||||
const subtitle = email?.trim() ? email.trim() : 'Lokaal account'
|
||||
const logoutFormRef = useRef<HTMLFormElement>(null)
|
||||
const [pendingLogout, startLogout] = useTransition()
|
||||
|
||||
// Server Action direct aanroepen — geen form/ref-dance. Eerdere implementatie
|
||||
// gebruikte een hidden form binnen DropdownMenuContent; die unmount op
|
||||
// onSelect en in deze base-ui-versie kwam de submit niet door.
|
||||
function handleLogout() {
|
||||
startLogout(async () => {
|
||||
await logoutAction()
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Form buiten DropdownMenuContent — die unmount op onSelect waardoor de ref
|
||||
null wordt voordat requestSubmit() vuurt. */}
|
||||
<form ref={logoutFormRef} action={logoutAction} className="hidden" />
|
||||
<DropdownMenu>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
className="rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
||||
aria-label="Accountmenu openen"
|
||||
|
|
@ -107,14 +112,15 @@ export function UserMenu({ userId, username, email, roles }: UserMenuProps) {
|
|||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuItem
|
||||
onSelect={() => logoutFormRef.current?.requestSubmit()}
|
||||
onClick={handleLogout}
|
||||
onSelect={handleLogout}
|
||||
disabled={pendingLogout}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<LogOut className="mr-2 h-4 w-4" />
|
||||
<span>Uitloggen</span>
|
||||
<span>{pendingLogout ? 'Uitloggen…' : 'Uitloggen'}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue