![](/img/trans.png)
[英]How to render dynamically generated inline SVG by using custom directive in angular 6
[英]How to dynamically import SVG and render it inline
我有一個 function 需要一些 arguments 並呈現 SVG。 我想根據傳遞給 function 的名稱動態導入 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 仍然返回但已解決。與 static 導入相比,模塊在調用導入之前不會執行() 已制成。”
這就是我的圖標解決的問題:
> Module
default: "static/media/antenna.11b95602.svg"
__esModule: true
Symbol(Symbol.toStringTag): "Module"
試圖以我的 function 試圖給我這個錯誤的方式呈現它:
Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.
我不明白如何使用這個導入的模塊將其渲染為組件,或者甚至可以這樣嗎?
導入 SVG 文件時,可以使用ref
和名為 export 的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 };
}
typescript 中的動態 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 };
}
對於那些在動態導入ReactComponent
時undefined
ReactComponent 的人,這是由於 Webpack 插件將ReactComponent
添加到每個以某種方式導入的 ZCD15A75C26008693647B31A3F0DE 的錯誤不會觸發動態導入。
基於此解決方案,我們可以通過在您的動態 SVG 導入上強制執行相同的加載程序來臨時解決它。
唯一的區別是ReactComponent
現在是default
的 output。
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`);
...
您的渲染函數(對於 class 組件)和 function 組件不應是異步的(因為它們必須返回 DOMNode 或 Z37A6259CC0C1DAE299A7866489DFFise0BDZ - 在您的情況下,它們返回 Prom。 相反,您可以以常規方式渲染它們,然后導入圖標並在下一次渲染中使用它。 嘗試以下操作:
const Test = () => {
let [icon, setIcon] = useState('');
useEffect(async () => {
let importedIcon = await import('your_path');
setIcon(importedIcon.default);
}, []);
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 => {
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;
};
動態加載 svg 的一種解決方案是使用require將其加載到img中,例如:
<img src={require(`../assets/${logoNameVariable}`)?.default} />
接受的解決方案不再適用於最新版本的 CRA(反應腳本 5):此處的問題示例: https://github.com/mattpodolak/webpack-svg
我以文本形式動態獲取 SVG 文件,然后將 SVG 放入 div dangerouslySetInnerHTML
中。
const Icon = ({ className, name, size = 16 }: IconProps) => {
const [Icon, setIcon] = React.useState("");
React.useEffect(() => {
fetch(`/icons/${name}.svg`)
.then((res) => res.text())
.then((res) => {
if (res.startsWith("<svg")) return setIcon(res);
console.error(
`Icon: "${name}.svg" not found in ${process.env.PUBLIC_URL}/icons`
);
return setIcon("");
});
}, [name]);
if (!Icon) return null;
return (
<div
className={classNames("icon", className)}
style={{ width: !size ? "100%" : size + "px", height: "100%" }}
dangerouslySetInnerHTML={{ __html: Icon }}
/>
);
};
您可以通過將填充值設置為“currentColor”來自動更改 svg 的顏色。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.