React动态加载SVG组件出现运行时错误,寻求技术帮助
解决React动态加载SVG组件的运行时错误
我来帮你分析下问题出在哪,以及怎么修复:
问题根源
你遇到的错误主要来自三个关键点:
- 初始渲染时组件未加载完成:
ImportedIconRef.current一开始是undefined,这时候直接渲染<SvgIcon />相当于让React渲染undefined,自然会抛出元素类型无效的错误。 - useRef不会触发重渲染:就算异步导入完成后更新了
ImportedIconRef.current,组件也不会重新渲染,所以永远显示不了加载好的SVG组件。 - (可选)SVG导入配置可能缺失:如果你的构建工具(Webpack/Vite)没有配置SVG作为React组件导入,
(await import(...)).ReactComponent会是undefined,也会导致同样的错误。
修复后的代码
我们改用useState来跟踪加载状态和导入的组件,同时添加加载/错误兜底:
import React, { useEffect, useState } from "react"; import { IIcon } from './IIcon'; import { DEFAULT_ASSET_ICON_FOLDER, } from '@/constants/default'; // 可以定义一个兜底的空组件或者错误图标组件 const FallbackIcon = () => <span>⚠️</span>; function Icon({name}: IIcon) { // 用state跟踪导入的组件和加载状态 const [SvgIcon, setSvgIcon] = useState<React.FC | null>(null); const [isLoading, setIsLoading] = useState(true); const [hasError, setHasError] = useState(false); useEffect(() => { const importIcon = async () => { setIsLoading(true); setHasError(false); try { // 确保你的构建工具配置了导出ReactComponent const importedModule = await import(`${DEFAULT_ASSET_ICON_FOLDER}/${name}.svg`); // 有些配置下可能是default导出,这里可以根据实际情况调整 const iconComponent = importedModule.ReactComponent || importedModule.default; setSvgIcon(iconComponent); } catch (err) { console.error(`Failed to load icon: ${name}`, err); setHasError(true); } finally { setIsLoading(false); } }; // 如果name为空,直接跳过加载 if (name) { importIcon(); } else { setIsLoading(false); setHasError(true); } }, [name]); // 加载中、错误、未加载完成时的兜底处理 if (isLoading) return <span>Loading...</span>; if (hasError || !SvgIcon) return <FallbackIcon />; return <SvgIcon />; } export default Icon;
关键改动说明
- 用useState替代useRef:state的更新会触发组件重渲染,这样当SVG组件加载完成后,组件会自动更新并渲染正确的图标。
- 添加加载/错误状态:避免初始渲染时的
undefined问题,同时给用户反馈加载状态或错误情况。 - 兼容不同的导出方式:有些构建工具配置下,SVG组件可能是
default导出,所以我们加了importedModule.ReactComponent || importedModule.default的兼容处理。
构建工具配置检查
确保你的项目构建工具正确配置了SVG转React组件:
- Webpack:需要安装
@svgr/webpack,并在webpack配置中添加规则:module.exports = { module: { rules: [ { test: /\.svg$/i, issuer: /\.[jt]sx?$/, use: ['@svgr/webpack'], }, ], }, }; - Vite:可以安装
@vitejs/plugin-react-svg,并在vite.config.ts中配置:import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import reactSvgPlugin from '@vitejs/plugin-react-svg'; export default defineConfig({ plugins: [react(), reactSvgPlugin({ defaultExport: 'component' // 确保导出的是React组件 })], });
这样修改后,你的<Icon name="DELETE" />应该就能正常工作了!
内容的提问来源于stack exchange,提问作者Peter J Harrison
相关产品推荐
相关产品推荐

