如何动态导入 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”:

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

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

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

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

原文由 Majoren 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.4k
2 个回答

您可以在导入 SVG 文件时使用 refReactComponent 命名导出。请注意,它必须是 ref 才能正常工作。

以下示例使用需要版本 v16.8 及更高版本的 React 挂钩。

示例动态 SVG 导入挂钩:

 function useDynamicSVGImport(name, options = {}) {
  const ImportedIconRef = useRef();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState();

  const { onCompleted, onError } = options;
  useEffect(() => {
    setLoading(true);
    const importIcon = async () => {
      try {
        ImportedIconRef.current = (
          await import(`./${name}.svg`)
        ).ReactComponent;
        if (onCompleted) {
          onCompleted(name, ImportedIconRef.current);
        }
      } catch (err) {
        if (onError) {
          onError(err);
        }
        setError(err);
      } finally {
        setLoading(false);
      }
    };
    importIcon();
  }, [name, onCompleted, onError]);

  return { error, loading, SvgIcon: ImportedIconRef.current };
}

编辑 react-dynamic-svg-import

打字稿中的示例动态 SVG 导入挂钩:

 interface UseDynamicSVGImportOptions {
  onCompleted?: (
    name: string,
    SvgIcon: React.FC<React.SVGProps<SVGSVGElement>> | undefined
  ) => void;
  onError?: (err: Error) => void;
}

function useDynamicSVGImport(
  name: string,
  options: UseDynamicSVGImportOptions = {}
) {
  const ImportedIconRef = useRef<React.FC<React.SVGProps<SVGSVGElement>>>();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error>();

  const { onCompleted, onError } = options;
  useEffect(() => {
    setLoading(true);
    const importIcon = async (): Promise<void> => {
      try {
        ImportedIconRef.current = (
          await import(`./${name}.svg`)
        ).ReactComponent;
        onCompleted?.(name, ImportedIconRef.current);
      } catch (err) {
        onError?.(err);
        setError(err);
      } finally {
        setLoading(false);
      }
    };
    importIcon();
  }, [name, onCompleted, onError]);

  return { error, loading, SvgIcon: ImportedIconRef.current };
}

编辑 react-dynamic-svg-import-ts


对于那些正在获取 undefined for ReactComponent 的人来说,当动态导入 SVG 时,这是由于 Webpack 插件添加了 ReactComponent 以某种方式导入的不会触发动态导入。

基于此 解决方案,我们可以通过在您的动态 SVG 导入上强制执行相同的加载程序来临时解决它。

唯一的区别是 ReactComponent 现在是 default 输出。

 ImportedIconRef.current = (await import(`!!@svgr/webpack?-svgo,+titleProp,+ref!./${name}.svg`)).default;


另请注意,使用带有可变部分的动态导入时存在限制。 This SO answer 详细解释了这个问题。

要解决此问题,您可以使动态导入路径更加明确。

例如,而不是

// App.js
<Icon path="../../icons/icon.svg" />

// Icon.jsx
...
import(path);
...

你可以把它改成

// App.js
<Icon name="icon" />

// Icon.jsx
...
import(`../../icons/${name}.svg`);
...

原文由 junwen-k 发布,翻译遵循 CC BY-SA 4.0 许可协议

您的渲染函数(对于类组件)和函数组件不应该是异步的(因为它们必须返回 DOMNode 或 null - 在您的情况下,它们返回一个 Promise)。相反,您可以以常规方式渲染它们,然后导入图标并在下一次渲染中使用它。尝试以下操作:

 const Test = () => {
  let [icon, setIcon] = useState('');

  useEffect(async () => {
    let importedIcon = await import('your_path');
    setIcon(importedIcon.default);
  }, []);

  return <img alt='' src={ icon }/>;
};

原文由 Enchew 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题