Reemplazar SVG con React Children.map

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

  1. 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.

  2. 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 si child.type es igual a "svg".

  3. 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, usando React.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.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *