7

I want to create a chart with amcharts5 in my react app.

I instantiate a root element of amcharts5 in a component that I import in my app component. I get the following error

You cannot have multiple Roots in the same DOM node

Here's my version:

"react": "^17.0.2"
"@amcharts/amcharts5": "^5.1.1"

Here's my code:

import { useLayoutEffect } from 'react'
import * as am5 from '@amcharts/amcharts5'

export default function AmCharts5() {
  useLayoutEffect(() => {
    let root = am5.Root.new('chartdiv')

    // root.current = root
    // here is a second Error : Property 'current' does not exist on type 'Root'


    return () => {
      root.dispose()
    }
  }, [])

  return <div id="chartdiv" style={{ width: '100%', height: '500px' }}></div>
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
LaïLoKen
  • 73
  • 1
  • 5
  • I just recreated your question in this sandbox (https://codesandbox.io/s/practical-nash-6ks15?file=/src/App.js) and it isn't giving me that error. You might want to check outside of this component, as this error tends to come up when nodes are rendered multiple times. – Alec Mather Feb 06 '22 at 20:53

4 Answers4

6

I had the same error, when I created second root element for my chart legend, but forgot to add the dispose method for this root element in useEffect return function. So in my case I solve this error by adding second dispose method in useEffect return function.

In my case useEffect has dependence on some data, and when I change it, useEffect runs again, and try to create second root element with the same name. And at after first render when I change someVar I have this error.

before:

useEffect(() => {
    const root = am5.Root.new("chart-pop");
    // ... some code

    const legendRoot = am5.Root.new("legend-div");
    // ... some code

    return () => root.dispose();
}, [someVar]);

After:

useEffect(() => {
    const root = am5.Root.new("chart-pop");
    // ... some code

    const legendRoot = am5.Root.new("legend-div");
    // ... some code

    return () => {root.dispose(); legendRoot.dispose();};
}, [someVar]);
2

A correct and simple way to fix this error is following the doc from AmCharts5 in the section 'Disposing'. Just put this code before your am5.ready(function() {...} and replace your divId =):

am5.array.each(am5.registry.rootElements, 
   function(root) {
      if (root.dom.id == divId) {
         root.dispose();
      }
   }
);
0

You should add dynamic id prop on chart component and use that in am5.Root.new(${props.id}) and add id={props.id} in return div that related to amchart.

Roman Mahotskyi
  • 4,576
  • 5
  • 35
  • 68
0

Another solution is save root in a global object and before create chart check if exist the chart, dispose that chart then create again.

 var MyGlobalObject={}

 function CrateMyChart(IdDivForDrawChart){
    
    if(MyGlobalObject[IdDivForDrawChart]){   //check if exist chart dispose that
                  MyGlobalObject[IdDivForDrawChart].dispose()
             }

    var  root = am5.Root.new("IdDivForDrawChart");
     // ... some code

     MyGlobalObject[IdDivForDrawChart]=root     // store chart in global object
}
henrry
  • 486
  • 6
  • 25