Resumen del Problema
El objetivo es crear un componente genérico en React que reemplace los elementos SVG dentro de un botón (usando shadcn
) por un ícono de carga (LoaderIcon
de lucid-react
). Aunque el intento es exitoso en algunas ocasiones, en otras no se logra debido a que child.type
no se resuelve correctamente para identificar los elementos SVG.
Problema Identificado
Cuando el código no funciona como se espera, principalmente se debe a que child.type
no es una referencia válida al tipo esperado. Esto puede suceder si no se está utilizando un componente SVG de manera adecuada o si hay problemas con el tipo de los hijos proporcionados al componente.
Soluciones Propuestas
-
Verificar el tipo de los hijos:
Asegurarse de que los hijos pasados al componente sean elementos válidos y que realmente sean SVG. Esto puede incluir el uso de una verificación más robusta para determinar si un hijo es un componente SVG. -
Uso de
React.Children.map
:
Modificar el enfoque de mapeo para que sea más flexible en la identificación de componentes SVG. Se puede considerar utilizar una comparación más específica que simplemente verificar sichild.type
es igual a"svg"
. - Implementación de una función auxiliar:
Crear una función auxiliar que valide si un componente es un SVG de manera más segura, usandoReact.isValidElement
y comprobando el tipo del componente directamente si es un componente de clase o un componente funcional.
Código Modificado
import { ReactNode, useMemo } from 'react';
import { Button } from 'shadcn';
import { Loader2Icon } from 'lucid-react';
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(
() =>
React.Children.map(children, (child) => {
if (React.isValidElement(child) && child.type && typeof child.type === 'string') {
const isSvg = child.type === 'svg'; // puede personalizarse para verificar componentes específicos
return isSvg ? (isPending ? <Loader2Icon className="animate-spin" /> : child) : child;
}
return child;
}),
[children, isPending]
);
return (
<Button {...props} onClick={onConfirm} disabled={isPending}>
{filteredChildren}
</Button>
);
}
Conclusión
Con estas mejoras, el componente ConfirmedActionButton
debería funcionar de manera más fiable al reemplazar los elementos SVG con un ícono de carga en situaciones donde la acción esté en curso. Es importante realizar pruebas exhaustivas con diferentes tipos de hijos para garantizar que el comportamiento se mantenga consistente.