diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8669b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +yarn.lock +node_modules diff --git a/SvgImage.js b/SvgImage.js index 964b036..81fd30e 100644 --- a/SvgImage.js +++ b/SvgImage.js @@ -1,46 +1,77 @@ // @flow +import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { View, WebView } from 'react-native'; +import Promise from 'bluebird'; +Promise.config({ cancellation: true }); // Need to explicitly enable this feature + +// TODO Make this something more precise oneOf(object,_?_), where _?_ is the type returned by `StyleSheet.create`. +const styleType = PropTypes.object; const firstHtml = ''; const lastHtml = ''; class SvgImage extends Component { - state = { fetchingUrl: null, svgContent: null }; + + static propTypes = { + source: PropTypes.shape({ + uri: PropTypes.string.isRequired + }).isRequired, + containerStyle: styleType, + style: styleType, + } + + state = { + svgContent: null, + fetchingPromise: null, + }; + componentDidMount() { - this.doFetch(this.props); + this.doFetch(); + } + + componentWillUnmount() { + const { fetchingPromise } = this.state || {}; + if(fetchingPromise) fetchingPromise.cancel(); } - componentWillReceiveProps(nextProps) { - this.doFetch(nextProps); + + componentDidUpdate(prevProps) { + const { source:oldSource } = prevProps || {}; + const { uri:oldUri } = oldSource || {}; + const { source:newSource } = this.props || {}; + const { uri:newUri } = newSource || {}; + if(oldUri !== newUri) this.doFetch(); } - doFetch = props => { - let uri = props.source && props.source.uri; + + doFetch() { + const { source } = this.props || {}; + const { uri } = source || {}; if (uri) { if (uri.match(/^data:image\/svg/)) { const index = uri.indexOf(' res.text()) - .then(text => { - this.setState({ fetchingUrl: uri, svgContent: text }); - }) - .catch(err => { - console.error('got error', err); - }); + this.setState(({fetchingPromise:previousFetch}) => ({ fetchingPromise: + Promise.resolve(fetch(uri)) + .call("text") + .then(text => this.setState({ svgContent: text })) + .catch(e => console.error(`Error fetching SVG URI: ${e.message||e}`, {uri, e})) + .return((previousFetch && previousFetch.isPending()) ? previousFetch : null) // Ensure we resolve/cancel previous fetch + })) } } - }; + } + render() { - const props = this.props; - const { svgContent } = this.state; - if (svgContent) { + const props = this.props || {}; + const { svgContent } = this.state || {}; + const hasSvgContent = Boolean(svgContent); return ( - - + { hasSvgContent && + /> } ); - } else { - return ( - - ); - } } } + export default SvgImage; diff --git a/package.json b/package.json index cbb416b..4a39a12 100644 --- a/package.json +++ b/package.json @@ -16,5 +16,13 @@ "react-native", "svg", "image" - ] + ], + "dependencies": { + "bluebird": "^3.5.1", + "debug": "^3.1.0", + "prop-types": "^15.6.2" + }, + "devDependencies": { + "supports-color": "^5.4.0" + } }