maikthomas
maikthomas

Reputation: 436

Render SVGSVGElement in React JS without dangerouslySetInnerHtml

Question: Can I render an SVGSVGElement in React without using dangerouslySetInnerHtml?

Context:

I am using the vis.js graph library, the getLegend method returns an SVGSVGElement object i.e. const icon = chart.getLegend(args); In the console I can see this:

in: icon instanceof SVGSVGElement
out: true
in: icon
out: <svg><rect x="0" y="0" width="30" height="30" class="vis-outline"></rect><path class="vis-graph-group0" d="M0,15 L30,15"></path></svg>

Problem:

When I try to render this in react using:

render (
<div> { icon } </div>
)

I get the following error:

Error: Objects are not valid as a React child (found: [object SVGSVGElement]). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of `LegendElement`

Workaround:

For now I am using: <svg dangerouslySetInnerHTML={{__html: icon.innerHTML}} />

But I was hoping for a straightforward solution that doesn't use a method with the word dangerous in the name.

Research:

I read this similar question but I don't think it helps for SVG generated at run time: How do I use an SVG in React without using dangerouslySetInnerHTML?

Upvotes: 14

Views: 4986

Answers (2)

w35l3y
w35l3y

Reputation: 8773

During development, it is very common to re-render the svg, so I had to do this:

  const icon = (...); // SVGSVGElement
  const svg = useRef(null);

  useEffect(() => {
    if (svg.current) {
      if (svg.current.firstChild) {
        svg.current.replaceChild(icon, svg.current.firstChild)
      } else {
        svg.current.appendChild(icon)
      }
    }
  }, []);

  return <div ref={svg} />

This code prevents from rendering 2+ times.

Upvotes: 1

Ihsan M&#252;jdeci
Ihsan M&#252;jdeci

Reputation: 974

You can simple append the SVGSVGElement using a useRef like this. This example is for a functional component with hooks but can be adapted for class components.

const svg = useRef(null);
useEffect(()=>{
    if(svg.current){
        svg.current.appendChild(icon)
    } 
}, []);

return (
    <div ref={svg}/>
);

Upvotes: 14

Related Questions