为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

慕妹3146593 2022-07-08 18:10:30
我有一个接受一些参数并呈现 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 但已经解析。与静态导入相比,模块在调用导入之前不会执行() 已制成。”这就是我的图标解决的问题:> Moduledefault: "static/media/antenna.11b95602.svg"__esModule: trueSymbol(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.我不明白如何使用这个导入的模块将其渲染为组件,或者甚至可以这样吗?
查看完整描述

4 回答

?
慕慕森

TA贡献1856条经验 获得超17个赞

导入 SVG 文件时可以使用ref并命名导出。ReactComponent请注意,它必须是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 };

}

打字稿中的示例动态 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 };

}

对于那些在动态导入 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`);

...


查看完整回答
反对 回复 2022-07-08
?
桃花长相依

TA贡献1860条经验 获得超8个赞

您的渲染函数(用于类组件)和函数组件不应是异步的(因为它们必须返回 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 }/>;

};


查看完整回答
反对 回复 2022-07-08
?
红颜莎娜

TA贡献1842条经验 获得超13个赞

我根据答案https://github.com/facebook/create-react-app/issues/5276#issuecomment-665628393进行了更改


export const Icon: FC<IconProps> = ({ name, ...rest }): JSX.Element | null => {

      const ImportedIconRef = useRef<FC<SVGProps<SVGSVGElement>> | any>();

      const [loading, setLoading] = React.useState(false);

      useEffect((): void => {

        setLoading(true);

        const importIcon = async (): Promise<void> => {

          try {

            // Changing this line works fine to me

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

          } catch (err) {

            throw err;

          } finally {

            setLoading(false);

          }

        };

        importIcon();

      }, [name]);


      if (!loading && ImportedIconRef.current) {

        const { current: ImportedIcon } = ImportedIconRef;

        return <ImportedIcon {...rest} />;

      }

      return null;

    };


查看完整回答
反对 回复 2022-07-08
?
LEATH

TA贡献1936条经验 获得超7个赞

动态加载 svg 的一种解决方案是使用require将其加载到img中,例如:

<img src={require(`../assets/${logoNameVariable}`)?.default} />


查看完整回答
反对 回复 2022-07-08
  • 4 回答
  • 0 关注
  • 393 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号