Reputation: 1899
I have a svg component that is declared like so:
import {ReactComponent as Icon} from 'someplace.svg'
function SomeComponent(props){
const [someState, setSomeState] = useState(0)
const iconRef = useRef(null)
useEffect(() => {
//always prints null
console.log(iconRef.current)
}, [someState])
return <div>
<button onClick={() => setSomeState(prev => prev + 1)}>{someState}</button>
<Icon ref={iconRef}/>
</div>
}
The problem here is that iconRef will always return null. I think this is because it is declared as a component so the ref would need to be forwarded directly to the svg tags but how do I do that?
Any ideas?
Upvotes: 6
Views: 14606
Reputation: 1833
I'm not sure if this works in create-react-app
, but it is dead anyway.
First you need to install svgr.
npm i @svgr/webpack
Add a svgr
config file.
./.svgrrc.js
module.exports = {
ref: true,
}
And a webpack config.
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
use: ['@svgr/webpack'],
},
],
},
}
If using next
insert the webpack config in next.config.js
.
const webpack = require('webpack');
module.exports = {
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
});
return config;
},
};
Then you can do something like:
import Icon from "@icons/icon.svg";
const IconWithRef () => {
const ref = useRef<SVGSVGElement | null>(null);
return (
<Icon width="50" ref={ref} />
);
};
You can check this sandbox for a working version.
UPDATE
Using this, I ran into some problems while using svg paths directly into .css
files, because webpack resolves it as a react component. The workaround I could find to make both it work is by changing the webpack
settings like so.
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
type: 'asset',
resourceQuery: /url/,
},
{
test: /\.svg$/i,
resourceQuery: { not: [/url/] },
use: ['@svgr/webpack'],
},
],
},
}
And then using svg
's paths in .css
like:
.svg-background {
background-image: url('../path/to/svg/image.svg?url');
}
Notice the ?url
query param at the end.
Using the option issuer: /\.[jt]sx?$/
, like in the docs, did not work for me.
Upvotes: 3
Reputation: 221
This could be fixed in 3 steps:
setRef
to it.Example:
Grab the SVG code into the component, like this:
const CloseIcon = (props) => (
<svg
width="38"
height="38"
viewBox="0 0 38 38"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="19" cy="19" r="18" stroke="#AFAFAF" stroke-width="2"></circle>
<path
d="M13.0548 13.336L24.9868 25.9185"
stroke="#AFAFAF"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M24.9862 13.3365L13.0542 25.9189"
stroke="#AFAFAF"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
></path>
</svg>
)
export default CloseIcon
Then, in the Parent Component, where you use this icon, set its ref property, like:
...
const closeIconRef = createRef()
...
<CloseIcon
style={{ position: 'absolute', top: 18, right: 18, cursor: 'pointer' }}
setRef={closeIconRef}
/>
Then add setRef
to the tag in your SVG component:
const CloseIcon = ({ setRef }) => (
<svg
ref={setRef}
width="38"
height="38"
viewBox="0 0 38 38"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
...
You are done!
*The important thing to remember: the child nodes still are non-referenced, so it works if there are no shapes on the way of a hit. You can attach a ref to every child tho.
Upvotes: 2