Resumen del Problema con React-Virtuoso y Framer-Motion
He implementado una lista virtual utilizando la biblioteca react-virtuoso
. Esta lista se renderiza dentro de una ventana modal, que a su vez tiene una animación de aparición y desaparición, implementada con framer-motion
. El problema surge cuando cierro la ventana modal: al hacerlo, aparece un error en la consola que dice: "react-virtuoso: Zero-sized element, this should not happen".
Diagnóstico
El error se produce porque react-virtuoso
requiere un tamaño definido para sus elementos, y al cerrar el modal, la lista virtual puede intentar renderizarse en un contenedor que no tiene su tamaño definido correctamente. Esto se relaciona con la manera en que AnimatePresence
gestiona la salida de los componentes animados.
Solución Propuesta
Para resolver este problema sin perder la animación proporcionada por AnimatePresence
, se sugiere utilizar un truco que consiste en asegurarse de que la lista virtual tenga sus dimensiones definidas antes de que se desmonten sus elementos. A continuación, se presentan los pasos para solucionar el problema:
-
Controlar el Estado de la Modalidad: Mantener el estado de la lista virtual sin desmontarla de inmediato. Esto permite evitar que la lista virtual intente renderizarse en un elemento de tamaño cero.
-
Retener las Dimensiones: Asegurarse de que el contenedor de la lista tenga siempre un tamaño definido, incluso cuando la modal se está cerrando.
- Uso de un Timeout: Implementar un pequeño retraso al cerrar la modal para permitir que la animación de salida complete su ciclo antes de desmontar la lista virtual.
Aquí está el código modificado de la ventana modal para que funcione sin errores:
import { FC, useEffect } from 'react';
import ReactDOM from 'react-dom';
import clsx from 'clsx';
import { AnimatePresence, motion, Variants } from 'framer-motion';
import { useAppDispatch, useAppSelector } from '../../store/hooks';
import { closeModal } from '../../store/slices/UISlice';
import styles from './Modal.module.sass';
interface ModalProps extends React.PropsWithChildren {
className?: string;
}
const overlayVariants: Variants = {
initial: {
opacity: 0,
},
animate: {
opacity: 1,
},
};
const wrapperVariants: Variants = {
initial: {
opacity: 0,
scale: 0,
filter: 'blur(5px)',
transition: {
duration: 0.5,
ease: [1, 0, 0, 1],
},
},
animate: {
opacity: 1,
scale: 1,
filter: 'blur(0px)',
transition: {
duration: 0.5,
ease: [1, 0, 0, 1],
},
},
};
export const Modal: FC<ModalProps> = ({ className, children }) => {
const { isModalOpen } = useAppSelector((state) => state.UISlice);
const dispatch = useAppDispatch();
useEffect(() => {
if (!isModalOpen) {
const timer = setTimeout(() => {
// Cerrar modal después de la animación
dispatch(closeModal());
}, 500); // Debe coincidir con la duración de la animación
return () => clearTimeout(timer); // Limpiar el timeout en caso de que el componente se desmonte
}
}, [isModalOpen, dispatch]);
return ReactDOM.createPortal(
<AnimatePresence>
{isModalOpen && (
<motion.div
variants={overlayVariants}
initial="initial"
animate="animate"
exit="initial"
className={clsx(className, styles.overlay)}
onClick={() => dispatch(closeModal())}
>
<motion.div
variants={wrapperVariants}
initial="initial"
animate="animate"
exit="initial"
className={styles.wrapper}
onClick={(e) => e.stopPropagation()}
>
{children}
</motion.div>
</motion.div>
)}
</AnimatePresence>,
document.body
);
};
Resumen de la Solución
Con esta modificación, el modal mantiene su animación de cierre sin provocar un error de "elemento sin tamaño" en react-virtuoso
. Asegúrate de que cualquier manipulación de estado relacionada con la lista virtual se gestiona de acuerdo con la lógica del modal para prevenir errores adicionales.