I would like to create a generic component that replaces the svgs inside a Button (shadcn
) with a LoaderIcon (lucid-react
). So sometimes it works, but sometimes not.
When it doesn't work, it is mostly because child.type
resolves to this :
Here is my current component :
export function ConfirmedActionButton<T>({
action,
actionParams,
children,
confirmOptions = {},
onSuccess,
...props
}: ButtonProps & {
action: (data: T) => Promise<FormActionState>
actionParams: T
confirmOptions?: ConfirmOptions
onSuccess?: () => void
}) {
const [isPending, startTransition] = useTransition()
const onConfirm = async () => {
const { ok } = await safeConfirm(confirmOptions)
if (ok) {
startTransition(async () => {
const res = await action(actionParams)
if (res?.error) {
toast.error("Une erreur est survenue.", { description: res.error })
}
if (res?.success) {
toast.success(res?.success)
onSuccess?.()
}
})
}
}
const filteredChildren = useMemo(
() =>
Children.map(children, (child) => {
if (isValidElement(child) && child.type === "svg") {
return isPending ? <Loader2Icon className="animate-spin" /> : child
}
return child
}),
[children, isPending]
)
return (
<Button {...props} onClick={onConfirm} disabled={isPending}>
{filteredChildren}
</Button>
)
}
the components usage :
<ConfirmedActionButton
className="absolute top-1.5 right-1.5 text-destructive rounded-full p-1"
size={"icon"}
variant={"ghost"}
action={deleteAction}
actionParams={asset.id}
>
<span className="sr-only">Delete asset</span>
<TrashIcon className="duration-200 ease-in-out" />
</ConfirmedActionButton>