猿问

如何动态导入 SVG 并内联渲染

我有一个接受一些参数并呈现 SVG 的函数。我想根据传递给函数的名称动态导入该 svg。它看起来像这样:


import React from 'react';


export default async ({name, size = 16, color = '#000'}) => {

  const Icon = await import(/* webpackMode: "eager" */ `./icons/${name}.svg`);

  return <Icon width={size} height={size} fill={color} />;

};

根据动态导入的 webpack 文档和神奇的评论“eager”:


“不生成额外的块。所有模块都包含在当前块中,并且不会发出额外的网络请求。仍然返回 Promise 但已经解析。与静态导入相比,模块在调用导入之前不会执行() 已制成。”


这就是我的图标解决的问题:


> Module

default: "static/media/antenna.11b95602.svg"

__esModule: true

Symbol(Symbol.toStringTag): "Module"

试图以我的函数试图给我这个错误的方式呈现它:


Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.


我不明白如何使用这个导入的模块将其渲染为组件,或者甚至可以这样吗?


慕妹3146593
浏览 394回答 4
4回答

慕慕森

导入 SVG 文件时可以使用ref并命名导出。ReactComponent请注意,它必须是ref为了使它工作。以下示例使用需要版本v16.8及更高版本的 React 钩子。示例动态 SVG 导入钩子:function useDynamicSVGImport(name, options = {}) {&nbsp; const ImportedIconRef = useRef();&nbsp; const [loading, setLoading] = useState(false);&nbsp; const [error, setError] = useState();&nbsp; const { onCompleted, onError } = options;&nbsp; useEffect(() => {&nbsp; &nbsp; setLoading(true);&nbsp; &nbsp; const importIcon = async () => {&nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; ImportedIconRef.current = (&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await import(`./${name}.svg`)&nbsp; &nbsp; &nbsp; &nbsp; ).ReactComponent;&nbsp; &nbsp; &nbsp; &nbsp; if (onCompleted) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onCompleted(name, ImportedIconRef.current);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; } catch (err) {&nbsp; &nbsp; &nbsp; &nbsp; if (onError) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onError(err);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; setError(err);&nbsp; &nbsp; &nbsp; } finally {&nbsp; &nbsp; &nbsp; &nbsp; setLoading(false);&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; };&nbsp; &nbsp; importIcon();&nbsp; }, [name, onCompleted, onError]);&nbsp; return { error, loading, SvgIcon: ImportedIconRef.current };}打字稿中的示例动态 SVG 导入钩子:interface UseDynamicSVGImportOptions {&nbsp; onCompleted?: (&nbsp; &nbsp; name: string,&nbsp; &nbsp; SvgIcon: React.FC<React.SVGProps<SVGSVGElement>> | undefined&nbsp; ) => void;&nbsp; onError?: (err: Error) => void;}function useDynamicSVGImport(&nbsp; name: string,&nbsp; options: UseDynamicSVGImportOptions = {}) {&nbsp; const ImportedIconRef = useRef<React.FC<React.SVGProps<SVGSVGElement>>>();&nbsp; const [loading, setLoading] = useState(false);&nbsp; const [error, setError] = useState<Error>();&nbsp; const { onCompleted, onError } = options;&nbsp; useEffect(() => {&nbsp; &nbsp; setLoading(true);&nbsp; &nbsp; const importIcon = async (): Promise<void> => {&nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; ImportedIconRef.current = (&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await import(`./${name}.svg`)&nbsp; &nbsp; &nbsp; &nbsp; ).ReactComponent;&nbsp; &nbsp; &nbsp; &nbsp; onCompleted?.(name, ImportedIconRef.current);&nbsp; &nbsp; &nbsp; } catch (err) {&nbsp; &nbsp; &nbsp; &nbsp; onError?.(err);&nbsp; &nbsp; &nbsp; &nbsp; setError(err);&nbsp; &nbsp; &nbsp; } finally {&nbsp; &nbsp; &nbsp; &nbsp; setLoading(false);&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; };&nbsp; &nbsp; importIcon();&nbsp; }, [name, onCompleted, onError]);&nbsp; return { error, loading, SvgIcon: ImportedIconRef.current };}对于那些在动态导入 SVG 时得到undefined的人来说ReactComponent,这是由于一个错误,即以某种方式将 SVG 添加ReactComponent到每个导入的 SVG 的 Webpack 插件不会在动态导入时触发。基于此解决方案,我们可以通过在您的动态 SVG 导入中强制使用相同的加载器来临时解决它。唯一的区别是ReactComponent现在是default输出。ImportedIconRef.current = (await import(`!!@svgr/webpack?-svgo,+titleProp,+ref!./${name}.svg`)).default;另请注意,使用带有可变部分的动态导入时存在限制。这个 SO answer详细解释了这个问题。要解决此问题,您可以使动态导入路径更加明确。例如,代替// App.js<Icon path="../../icons/icon.svg" />// Icon.jsx...import(path);...您可以将其更改为// App.js<Icon name="icon" />// Icon.jsx...import(`../../icons/${name}.svg`);...

桃花长相依

您的渲染函数(用于类组件)和函数组件不应是异步的(因为它们必须返回 DOMNode 或 null - 在您的情况下,它们返回一个 Promise)。相反,您可以以常规方式渲染它们,然后导入图标并在下一次渲染中使用它。尝试以下操作:const Test = () => {&nbsp; let [icon, setIcon] = useState('');&nbsp; useEffect(async () => {&nbsp; &nbsp; let importedIcon = await import('your_path');&nbsp; &nbsp; setIcon(importedIcon.default);&nbsp; }, []);&nbsp; return <img alt='' src={ icon }/>;};

红颜莎娜

我根据答案https://github.com/facebook/create-react-app/issues/5276#issuecomment-665628393进行了更改export const Icon: FC<IconProps> = ({ name, ...rest }): JSX.Element | null => {&nbsp; &nbsp; &nbsp; const ImportedIconRef = useRef<FC<SVGProps<SVGSVGElement>> | any>();&nbsp; &nbsp; &nbsp; const [loading, setLoading] = React.useState(false);&nbsp; &nbsp; &nbsp; useEffect((): void => {&nbsp; &nbsp; &nbsp; &nbsp; setLoading(true);&nbsp; &nbsp; &nbsp; &nbsp; const importIcon = async (): Promise<void> => {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Changing this line works fine to me&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ImportedIconRef.current = (await import(`!!@svgr/webpack?-svgo,+titleProp,+ref!./${name}.svg`)).default;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } catch (err) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw err;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } finally {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setLoading(false);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; };&nbsp; &nbsp; &nbsp; &nbsp; importIcon();&nbsp; &nbsp; &nbsp; }, [name]);&nbsp; &nbsp; &nbsp; if (!loading && ImportedIconRef.current) {&nbsp; &nbsp; &nbsp; &nbsp; const { current: ImportedIcon } = ImportedIconRef;&nbsp; &nbsp; &nbsp; &nbsp; return <ImportedIcon {...rest} />;&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; return null;&nbsp; &nbsp; };

LEATH

动态加载 svg 的一种解决方案是使用require将其加载到img中,例如:<img&nbsp;src={require(`../assets/${logoNameVariable}`)?.default}&nbsp;/>
随时随地看视频慕课网APP

相关分类

JavaScript
我要回答