简体   繁体   中英

React Import SVG inline from URL

I am working on an issue where SVG image needs to be loaded from URL(AWS S3) to a react component. I am able to successfully show and load image using the SVG-inline react component from a local file. However, the svg files needs to be loaded inline from S3 bucket, JS svg import does not work with URLs.

So I am trying to find the best alternative solution for this?

You should fetch svg image and paste it in html

const SVG_URL = url = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/106114/tiger.svg';

class Svg extends React.Component {
  state = {
    svg: null,
    loading: false,
  }

  componentDidMount() {
    fetch(this.props.url)
      .then(res => res.text())
      .then(text => this.setState({ svg: text }));
  }

  render() {
    const { loading, svg } = this.state;
    if (loading) {
      return <div className="spinner"/>;
    } else if(!svg) {
      return <div className="error"/>
    }
    return <div dangerouslySetInnerHTML={{ __html: this.state.svg}}/>;
  }
}

ReactDOM.render(
  <Svg url='https://s3-us-west-2.amazonaws.com/s.cdpn.io/106114/tiger.svg' />,
  document.querySelector("#app")
);

you can can edit example here https://jsfiddle.net/anu6x3bk/

React does not recommend using dangerouslySetInnerHTML so you should use some package like svg-inline-react

@ivnkld answer pointed me to the right direction, thanks! And for Googlers such as I was, here is implementation of the same approach with hooks:

import React, { useEffect, useState } from 'react';

const SvgInline = props => {
    const [svg, setSvg] = useState(null);
    const [isLoaded, setIsLoaded] = useState(false);
    const [isErrored, setIsErrored] = useState(false);

    useEffect(() => {
        fetch(props.url)
            .then(res => res.text())
            .then(setSvg)
            .catch(setIsErrored)
            .then(() => setIsLoaded(true))
    }, [props.url]);

    return (
        <div 
            className={`svgInline svgInline--${isLoaded ? 'loaded' : 'loading'} ${isErrored ? 'svgInline--errored' : ''}`}
            dangerouslySetInnerHTML={{ __html: svg }}
        />
    );
}

For those who want to avoid using dangerouslySetInnerHTML please check the following example. Once we get the svg as text from the api, we will use DOMParser to convert the text to Document object. We will then use React.createElement to render the image (or) icon.

// utils/convertDocEleToReact.js
import { createElement } from "react";

const innerFunction = (element, props) => {
    const tagName = element.tagName;
    let _props = props || {};

    for(let i = 0; i < element.attributes.length; i++){
      _props[element.attributes[i].nodeName] = element.attributes[i].nodeValue;
    }

    let children = Array.from(element.children).map(item => innerFunction(item));

    return createElement(tagName, _props, children);
};

export const convertDocEleToReact = (element, props) => {
    try{
       return innerFunction(element, props);
    }
    catch(ex){
      return createElement("span", {}, "Error loading svg image");
    }
};
// components/SVGUrl.js
import React from "react";
import { convertDocEleToReact } from "./utils/convertDocEleToReact";

export const SVGUrl = ({ ...props }) => {
    const [ Comp, setComp ] = useState(null);
    const [loading, setLoading] = useState(false);

    useEffect(_ => {
        setLoading(true);
        fetch(/** svg url */)
          .then(res => res.text())
          .then(res => {
              const domParser = new DOMParser();
              const ele = domParser.parseFromString(res, "image/svg+xml");
              setComp(convertDocEleToReact(ele.documentElement, props));
              setLoading(false);
          });
    }, []);

    return loading ? "..." : Comp;
};

Now React support SVG just use <img/> tag, something like below.

<img src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/106114/tiger.svg' />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM