pd4d10 / vite-plugin-svgr

Vite plugin to transform SVGs into React components
MIT License
515 stars 55 forks source link

Dynamic SVG imports? #72

Open wmonecke opened 1 year ago

wmonecke commented 1 year ago

Is there a way to import svgs dynamically?

I would love to be able to do something like this:

dynamic import hook

import { FunctionComponent } from 'preact';
import { useEffect, useRef, useState } from 'preact/hooks';

type Props = {
  name: string;
};

export const useDynamicSVGImport = ({ name }: Props) => {
  const ImportedIconRef = useRef<
    FunctionComponent<SVGSVGElement> | undefined
  >();

  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error>();

  useEffect(() => {
    setLoading(true);

    const importIcon = async (): Promise<void> => {
      try {
        const imported = await import(`../../assets/icons/${name}.svg`).then(
          module => module.default,
        );

        // logs: /src/assets/icons/spinner.svg if I pass name = spinner
        console.log('imported', imported); 

        ImportedIconRef.current = imported;

      } catch (err) {
        console.log(err);

        setError(err);
      } finally {
        setLoading(false);
      }
    };

    importIcon();
  }, [name]);

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

Note: If I pass SvgIcon to the src of an img tag it renders the icon properly so it is being imported.

icon.tsx

import { FunctionComponent } from 'preact';

import { useDynamicSVGImport } from '~/hooks/icons/use-dynamic-import';

type Props = {
  name: string;
  className?: string;
  isOutlineIcon?: boolean;
};

export const Icon: FunctionComponent<Props> = ({
  name,
  className,
  isOutlineIcon = false,
  ...restProps
}) => {
  const { error, loading, SvgIcon } = useDynamicSVGImport({ name });

  if (error || !SvgIcon || loading) {
    return null;
  }

  return (
    <SvgIcon
      className={`
        fill-slate-400
        ${isOutlineIcon ? '' : 'fill-current'}
        ${className ? className : 'h-5 w-5 text-lightBlack'}`}
      {...restProps}
    />
  );
};

This is currently not working.

How can I convert the imported file into a component?

    const imported = await import(`../../assets/icons/${name}.svg`).then(
          module => module.default,
        );
flydev-fr commented 1 year ago

@wmonecke you could take a look on this example on stackblitz and answer on stackoverflow to check if it fit your needs.

pd4d10 commented 1 year ago

Would Vite glob import help?

GideonMax commented 6 months ago

aren't you supposed to import with ?react for example: import Logo from "./logo.svg?react"

nogess commented 1 month ago

有没有办法动态导入 svgs?

我很想能够做这样的事情:

动态导入钩子

import { FunctionComponent } from 'preact';
import { useEffect, useRef, useState } from 'preact/hooks';

type Props = {
  name: string;
};

export const useDynamicSVGImport = ({ name }: Props) => {
  const ImportedIconRef = useRef<
    FunctionComponent<SVGSVGElement> | undefined
  >();

  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error>();

  useEffect(() => {
    setLoading(true);

    const importIcon = async (): Promise<void> => {
      try {
        const imported = await import(`../../assets/icons/${name}.svg`).then(
          module => module.default,
        );

        // logs: /src/assets/icons/spinner.svg if I pass name = spinner
        console.log('imported', imported); 

        ImportedIconRef.current = imported;

      } catch (err) {
        console.log(err);

        setError(err);
      } finally {
        setLoading(false);
      }
    };

    importIcon();
  }, [name]);

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

注意:如果我传递SvgIconsrc标签,img它会正确呈现图标,以便被导入。

图标.tsx

import { FunctionComponent } from 'preact';

import { useDynamicSVGImport } from '~/hooks/icons/use-dynamic-import';

type Props = {
  name: string;
  className?: string;
  isOutlineIcon?: boolean;
};

export const Icon: FunctionComponent<Props> = ({
  name,
  className,
  isOutlineIcon = false,
  ...restProps
}) => {
  const { error, loading, SvgIcon } = useDynamicSVGImport({ name });

  if (error || !SvgIcon || loading) {
    return null;
  }

  return (
    <SvgIcon
      className={`
        fill-slate-400
        ${isOutlineIcon ? '' : 'fill-current'}
        ${className ? className : 'h-5 w-5 text-lightBlack'}`}
      {...restProps}
    />
  );
};

目前这不起作用。

如何将导入的文件转换为组件?

    const imported = await import(`../../assets/icons/${name}.svg`).then(
          module => module.default,
        );

Need to downgrade the plugin to version 2.x, the latest 4.x version doesn't work with dynamic introduction