以Arco Design为例,为什么配置了 unmountOnExit 但 Modal 仍未销毁 DOM。以下是详细的排查步骤:
1. 检查 Modal 的完整配置
import { Modal } from '@arco-design/web-react';
const YourComponent = () => {
const [visible, setVisible] = useState(false);
return (
<Modal
visible={visible}
unmountOnExit={true} // 明确设置为 true
onCancel={() => {
setVisible(false);
// 确保 visible 状态被正确更新
console.log('Modal closing, visible:', visible);
}}
afterClose={() => {
// 添加 afterClose 回调来确认 Modal 是否正确关闭
console.log('Modal closed');
}}
destroyOnClose={true} // 某些组件库需要这个属性
>
{/* Modal content */}
</Modal>
);
};
2. 添加生命周期检查
const YourComponent = () => {
const [visible, setVisible] = useState(false);
useEffect(() => {
console.log('Modal visible state changed:', visible);
return () => {
console.log('Modal component cleanup');
};
}, [visible]);
return (
<Modal
visible={visible}
unmountOnExit
// ... other props
>
<ModalContent />
</Modal>
);
};
const ModalContent = () => {
useEffect(() => {
console.log('ModalContent mounted');
return () => {
console.log('ModalContent unmounted');
};
}, []);
return <div>Modal content</div>;
};
3. 检查父组件的渲染逻辑
const ParentComponent = () => {
// 确保父组件的重渲染不会影响 Modal 的销毁
const [parentState, setParentState] = useState(false);
return (
<div>
<YourComponent key="modal" /> // 添加 key 确保组件正确重渲染
{/* other content */}
</div>
);
};
4. 使用 React.memo 优化渲染
const ModalContent = React.memo(() => {
// Modal 内容
return <div>Modal content</div>;
});
const YourComponent = () => {
const [visible, setVisible] = useState(false);
const handleCancel = useCallback(() => {
setVisible(false);
}, []);
return (
<Modal
visible={visible}
unmountOnExit
onCancel={handleCancel}
>
<ModalContent />
</Modal>
);
};
5. 检查是否有异步操作阻止销毁
const YourComponent = () => {
const [visible, setVisible] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const handleCancel = async () => {
setIsLoading(true);
try {
// 确保异步操作完成后才关闭 Modal
await someAsyncOperation();
setVisible(false);
} finally {
setIsLoading(false);
}
};
return (
<Modal
visible={visible}
unmountOnExit
onCancel={handleCancel}
confirmLoading={isLoading}
>
{/* Modal content */}
</Modal>
);
};
6. 强制更新方案
const YourComponent = () => {
const [visible, setVisible] = useState(false);
const [key, setKey] = useState(0);
const handleCancel = () => {
setVisible(false);
// 强制重新渲染 Modal
setKey(prev => prev + 1);
};
return (
<Modal
key={key}
visible={visible}
unmountOnExit
onCancel={handleCancel}
>
{/* Modal content */}
</Modal>
);
};
7. 使用 Portal 检查
import ReactDOM from 'react-dom';
const ModalPortal = ({ children, visible }) => {
const [mounted, setMounted] = useState(false);
const containerRef = useRef(null);
useEffect(() => {
if (visible && !mounted) {
containerRef.current = document.createElement('div');
document.body.appendChild(containerRef.current);
setMounted(true);
} else if (!visible && mounted) {
containerRef.current?.remove();
setMounted(false);
}
}, [visible, mounted]);
if (!mounted) return null;
return ReactDOM.createPortal(
children,
containerRef.current
);
};
8. 检查样式覆盖
// 确保没有样式覆盖导致 Modal 看起来像被销毁
.arco-modal-wrapper {
display: none !important; // 避免使用这样的样式覆盖
}
9. 添加调试代码
const YourComponent = () => {
const modalRef = useRef(null);
useEffect(() => {
// 监控 Modal DOM 节点
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
console.log('DOM changed:', mutation);
});
});
if (modalRef.current) {
observer.observe(modalRef.current, {
childList: true,
subtree: true
});
}
return () => observer.disconnect();
}, []);
return (
<Modal
ref={modalRef}
// ... other props
>
{/* Modal content */}
</Modal>
);
};
总结
总结以上,比较隐晦的情况是异步请求或者异步数据阻止了组件销毁,尤其是存在轮训的情况下。