Created
January 27, 2018 16:05
-
-
Save wmertens/37c63d8a7088b2bfa3af298fe49a6c98 to your computer and use it in GitHub Desktop.
Use svg symbols in React, works without changes with SSR
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import arrow from 'symbol-loader!arrow.svg' | |
const App = () => ( | |
<SymbolProvider> | |
<svg><Use symbol={arrow} /></svg> | |
</SymbolProvider> | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict' | |
const path = require('path') | |
const SVGCompiler = require('svg-baker') | |
const svgCompiler = new SVGCompiler() | |
const getFileId = filePath => path.parse(filePath).name | |
module.exports = function(content) { | |
const resourceQuery = this.resourceQuery || '' | |
const done = this.async() | |
const id = getFileId(this.resourcePath) | |
svgCompiler | |
.addSymbol({id, content, path: this.resourcePath + resourceQuery}) | |
.then(symbol => { | |
const wrappedId = JSON.stringify(symbol.id) | |
const text = JSON.stringify(symbol.render()) | |
done(null, `module.exports = {id: ${wrappedId}, text: ${text}}`) | |
}) | |
.catch(done) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {Component} from 'react' | |
import PropTypes from 'prop-types' | |
import Symbols from 'plugins/icons/Symbols' | |
class SymbolProvider extends Component { | |
static childContextTypes = { | |
icons: PropTypes.object, | |
} | |
uses = {} | |
childCtx = { | |
icons: { | |
register: fn => (this.onUpdate = fn), | |
unRegister: fn => { | |
if (this.onUpdate === fn) this.onUpdate = null | |
}, | |
addSymbol: symbol => { | |
const ref = this.uses[symbol.id] | |
if (ref) { | |
if (ref.symbol !== symbol) { | |
console.error(new Error(`Symbol ID ${symbol.id} has to be unique`)) | |
return | |
} | |
ref.count++ | |
} else { | |
this.uses[symbol.id] = {symbol, count: 1} | |
} | |
if (this.onUpdate) this.onUpdate() | |
}, | |
removeSymbol: symbol => { | |
const ref = this.uses[symbol.id] | |
if (!ref) { | |
console.log(new Error(`refcount error for symbol ${symbol.id}`)) | |
return | |
} | |
ref.count-- | |
if (ref.count < 1) delete this.uses[symbol.id] | |
}, | |
getSymbols: () => | |
Object.values(this.uses) | |
.map(s => s.symbol.text) | |
.join(''), | |
}, | |
} | |
getChildContext() { | |
return this.childCtx | |
} | |
render() { | |
const {children} = this.props | |
return ( | |
<React.Fragment> | |
{children} | |
<Symbols /> | |
</React.Fragment> | |
) | |
} | |
} | |
export default SymbolProvider |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {Component} from 'react' | |
import PropTypes from 'prop-types' | |
class SvgSymbols extends Component { | |
static contextTypes = { | |
icons: PropTypes.object, | |
} | |
update = () => this.forceUpdate() | |
componentDidMount() { | |
this.context.icons.register(this.update) | |
} | |
componentWillUnmount() { | |
this.context.icons.unregister(this.update) | |
} | |
render() { | |
const {getSymbols} = this.context.icons | |
return ( | |
<svg | |
xmlns="http://www.w3.org/2000/svg" | |
xmlnsXlink="http://www.w3.org/1999/xlink" | |
dangerouslySetInnerHTML={{__html: getSymbols()}} | |
/> | |
) | |
} | |
} | |
export default SvgSymbols |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {Component} from 'react' | |
import PropTypes from 'prop-types' | |
class Use extends Component { | |
static contextTypes = { | |
icons: PropTypes.object, | |
} | |
updateSymbols(oldSymbol, newSymbol) { | |
if (oldSymbol === newSymbol) return | |
const {addSymbol, removeSymbol} = this.context.icons | |
if (oldSymbol) removeSymbol(oldSymbol) | |
if (newSymbol) addSymbol(newSymbol) | |
} | |
componentWillMount() { | |
this.updateSymbols(null, this.props.symbol) | |
} | |
componentWillReceiveProps(nextProps) { | |
this.updateSymbols(this.props.symbol, nextProps.symbol) | |
} | |
componentWillUnmount() { | |
this.updateSymbols(this.props.symbol, null) | |
} | |
render() { | |
const {symbol} = this.props | |
if (!symbol) return false | |
return <use xlinkHref={'#' + symbol.id} /> | |
} | |
} | |
export default Use |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This works because:
<use>
can refer to symbols that follow it in the page<Symbols/>
, all the symbols used in the page are known