typescript 我可以用动态import()表达式将SVG文件作为react组件导入吗?

xwbd5t1u  于 2023-01-31  发布在  TypeScript
关注(0)|答案(5)|浏览(490)

从stackoverflow的this answer中,我得到了一个关于如何将svg导入为 ReactComponent 并更改其颜色/宽度等的解决方案。
但是对于动态导入是否可以做同样的事情呢?我的功能组件:

import * as React from 'react';
import SvgIconComponent from './SvgIcon.Interface';

import {ReactComponent} from '*.svg';

const SvgIcon: React.FC<SvgIconComponent> =({width, color, name}) =>
{
    import(`../../assets/icons/${name}.svg`).then((Icon) => {
        return <Icon fill={color} width={width}/>
    });
};

export default SvgIcon;

在当前实现中,我得到错误:

TS2749: 'ReactComponent' refers to a value, but is being used as a type here. // .then((Icon as ReactComponent)

TS2604: JSX element type 'Icon' does not have any construct or call signatures. // .then(Icon)
2ic8powd

2ic8powd1#

我建议你使用process.env.PUBLIC_URL来渲染你的svg。把你所有的svg文件放在public文件夹里,比如说public/svgFiles
然后可以将其用作<img src={process.env.PUBLIC_URL + "/svgFiles/" + file_name} alt="project" />
由于公共文件夹中的所有内容都包含在最终的build中,所以所有的svg文件以及您使用的路径都将得到处理,由于所有内容都将从公共文件夹引用,因此构建后路径不会更改。
方法二
通过以下方式将SVG作为react组件导入:

import { ReactComponent as MY_SVG_COMPONENT } from '/path/to/svg'

现在您可以传递SVGComponent中可用的所有 prop ,例如

<MY_SVG_COMPONENT className="my-svg-class" />
pokxtpni

pokxtpni2#

我认为使用React组件来制作颜色和宽度的动态图标是一种更好的方法。
我添加示例代码:

export const TwitterIcon = ({color, width}: Props) => (
    <svg
        width={width ? width : 24}
        height={width ? width : 24}
        viewBox="0 0 24 24"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
    >
        <path
            fill={color ? color : "#1f1f1f"}
            d="M8.0798 19.9998C9.55849 20.0493 11.0321 19.8018 12.4135 19.2721C13.795 18.7424 15.0562 17.9411 16.1227 16.9156C17.1892 15.8901 18.0393 14.6613 18.6228 13.3017C19.2063 11.9421 19.5113 10.4793 19.5198 8.9998C20.1974 8.16121 20.7006 7.19563 20.9998 6.1598C21.0221 6.07796 21.0203 5.99141 20.9946 5.91057C20.9689 5.82974 20.9203 5.75806 20.8548 5.70417C20.7893 5.65027 20.7096 5.61647 20.6253 5.60683C20.5411 5.59718 20.4558 5.6121 20.3798 5.6498C20.0253 5.82045 19.6262 5.87558 19.2386 5.80741C18.8511 5.73923 18.4948 5.5512 18.2198 5.2698C17.8687 4.88538 17.4441 4.57533 16.971 4.35803C16.498 4.14072 15.9861 4.02059 15.4657 4.00473C14.9454 3.98888 14.4272 4.07763 13.9417 4.26574C13.4563 4.45384 13.0136 4.73746 12.6398 5.0998C12.128 5.59546 11.7531 6.21509 11.5516 6.89847C11.3501 7.58186 11.3288 8.30575 11.4898 8.9998C8.1398 9.1998 5.8398 7.6098 3.9998 5.4298C3.94452 5.3672 3.87221 5.32205 3.7917 5.29987C3.71119 5.27769 3.62596 5.27943 3.54642 5.30488C3.46688 5.33033 3.39648 5.37839 3.3438 5.4432C3.29113 5.508 3.25846 5.58674 3.2498 5.6698C2.89927 7.61422 3.15213 9.61935 3.97442 11.4159C4.7967 13.2124 6.14904 14.7143 7.8498 15.7198C6.70943 17.0276 5.10801 17.8445 3.3798 17.9998C3.28721 18.0152 3.20174 18.0591 3.13535 18.1254C3.06896 18.1917 3.02497 18.2772 3.00954 18.3698C2.99411 18.4623 3.00801 18.5574 3.0493 18.6417C3.09059 18.726 3.15719 18.7952 3.2398 18.8398C4.74332 19.5912 6.39903 19.988 8.0798 19.9998Z"
        />
    </svg>
);

如果你想制作图标库

interface IconProps {
   keyword: string; //make the clear type to make switch
   color: string;
   width: number;
}

const SvgIcon = ({keyword, color, width}: IconProps) => {
   // In this case you have to think about the switch and types in typescript.
    return (
       {
           'twitter': <TwitterIcon color={color} width={width}/>,
           'facebook': <FacebookIcon color={color} width={width}/>,
       }[keyword]
   )
}

//in component
<SvgIcon keyword='twitter' width={24} color='red'/>

如果你有太多的SVG图标,我没有任何解决方案,我的手现在。如果我有任何好主意,我会再次张贴在这里。

zbq4xfa0

zbq4xfa03#

从我所看到的你的问题是,你导入整个文件,并试图把它呈现为一个组件,你需要附加键/组件标识符/常量名称到require.例如,如果你使用export defualt YourSVGComponent,你可以通过以下操作动态访问它:

import * as React from 'react';
import SvgIconComponent from './SvgIcon.Interface';

const SvgIcon: React.FC<SvgIconComponent> =({width, color, name}) =>
{
    const Icon = require(`../../assets/icons/${name}.svg`).default
    return <Icon fill={color} width={width}/>
};

export default SvgIcon;
yeotifhr

yeotifhr4#

这里有许多错误。
∮ ∮ ∮ ∮ ∮一个月一个月
您在链接答案中看到的ReactComponent实际上是Create React应用程序加载.svg文件时的一个属性。这是一个超级整洁的功能,我今天才第一次了解它!
在该示例代码中

import { ReactComponent as Logo } from './logo.svg';

他们所做的是从'./logo.svg'导入中获取ReactComponent属性,并将其重命名为Logo
您的import {ReactComponent} from '*.svg';没有多大意义。
ReactComponent用作类型时,会出现"type as value"错误,因为它不是类型。您尝试使用.then((Icon as ReactComponent)执行的类型Assert应该是.then((Icon as React.ComponentType<SomePropsType>)

一米十一秒

让我们花点时间看看从import语句中实际得到了什么。

import(`../../assets/icons/${name}.svg`).then(console.log);

您应该看到一个具有三个属性的对象:一米十二氮一x、一米十三氮一x和一米十四氮一x。

{
  ReactComponent: ƒ SvgLogo(),
  default: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0i...",
  Url: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0i..."
}

从svg文件导入的default(也是Url属性)是图像的data-url,可以在imgsrc属性中使用。
ReactComponent是我们要找的属性!这就是Create React App将svg转换为React组件的地方。(注意:名称"SvgLogo"取决于文件的名称)。

一米二十分

SvgIcon函数没有返回任何内容。您拥有的return语句是then回调的返回。
处理异步的import有点棘手,因为你在第一次渲染时没有值,在这种情况下,我们可以返回null(或者你可能想返回一个大小的占位符)。
这里我使用element状态来存储返回的元素,它以null开始,在加载后被替换为<Icon/>
我在一个依赖于你的 prop 的useEffect中调用import。一旦导入解析,我们就从res.ReactComponent中得到Icon
Icon的推断类型是any,这对您来说可能很好。如果您想要一个准确的类型,可以使用凌乱的as React.ComponentType<JSX.IntrinsicElements['svg']>;,它表示"这是一个React组件,它接受<svg>元素的所有属性"。

import * as React from "react";

interface SVGIconProps {
  width: string | number;
  color: string;
  name: string;
}

const SvgIcon: React.FC<SVGIconProps> = ({ width, color, name }) => {
  const [element, setElement] = React.useState<JSX.Element | null>(null);

  React.useEffect(() => {
    import(`./icons/${name}.svg`).then((res) => {
      const Icon = res.ReactComponent as React.ComponentType<JSX.IntrinsicElements['svg']>;
      setElement(<Icon fill={color} width={width} />);
    });
  }, [name, color, width]);

  return element;
};

export default SvgIcon;
enyaitl3

enyaitl35#

import MyIcon from '../../assets/icons/${name}.svg'

<MyIcon />

相关问题