Ibra
Ibra

Reputation: 1072

Chart Duplicates because of useEffect re-render

I have a parent component which I'm using to pass props (i.e backgroundColor) to child component( <LivePortfolioGraph />).

In my child component I have a dependency array that re-renders every time color is changed from parent i.e (props.backgroundColor).

Now i don't want want to create a new graph on every render, if chart exist just apply color options.

Really appreciate any help! Thanks.

Parent Component

    const Main = (props) => {
    
      const [backgroundColor, setbackgroundColor] = useState('#141d27');
    
     const g = useRef(null);
    
    return (
    
     <div ref={g} className="FinancialChartIntro" >
    
        <LivePortfolioGraph g={g} backgroundColor={backgroundColor} />
    
        </div> 
    
    )
}

Child Component

const  LivePortfolioGraph = (props) => {

const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);


useEffect( ()=> {

const handleStyle = _debounce(() => { chart.applyOptions({layout: {backgroundColor:props.backgroundColor  } }); }, 100)

window.addEventListener("input", handleStyle); 


const chart = createChart(props.g.current, options );


// Clean Up
return () => {


window.removeEventListener('input', handleStyle);

}



}, [props.backgroundColor])

First Render

When I change color, another graph shows up with the previous defaultValue

Element Created

UPDATE I have found a hacky way to remove child nodes( chart dups). It slows down the page like crazy I dont wanna even use it. STILL TRYING to find another solution.

> if(props.g.current.childNodes.length > 2) {
> props.g.current.removeChild(props.g.current.childNodes[2])  }

Upvotes: 2

Views: 1391

Answers (3)

klugjo
klugjo

Reputation: 20885

Using the chart remove() method before any new initialization works for me:

  const chartRef = useRef<HTMLDivElement>(null);
  const chart = useRef<IChartApiBase>();

  useEffect(() => {
    if (!chartRef.current) return;

    chart.current?.remove();
    chart.current = createChart(chartRef.current, {...});

    // chart stuff

    chart.current.timeScale().fitContent();
  }, [chartRef.current, ...dependencies]);

Upvotes: 1

Daniel Cooke
Daniel Cooke

Reputation: 1634

Just perform a check to see if the chart has already be instantiated.

Here is a snippet of a hook I created to setup the chart useSetupChart

  useEffect(() => {
    if (!chartContainerRef.current || chart) return;

    setChart(createChart(chartContainerRef.current));
  }, [chartContainerRef.current, chart]);

Upvotes: 1

Drew Reese
Drew Reese

Reputation: 203227

I think your issue is the single useEffect hook is doing "too much" work. Move the chart value into a React ref and use the effect hook to update the color/styling.

const  LivePortfolioGraph = (props) => {
  const [width, setWidth] = React.useState(0);
  const [height, setHeight] = React.useState(0);

  const chart = React.useRef(createChart(props.g.current, options);

  React.useEffect(()=> {
    const handleStyle = _debounce(() => {
      chart.current.applyOptions({
        layout: {
          backgroundColor: props.backgroundColor,
        },
      });
    }, 100);

    window.addEventListener("input", handleStyle); 
    // Clean Up
    return () => {
      window.removeEventListener('input', handleStyle);
    }
}, [props.backgroundColor]);

Upvotes: 1

Related Questions