Search code examples
javascriptreactjslightweight-charts

Chart draws again every time I change the state of a component


The problem is when i click on button to use method changeState, state changes, MoreInfo component shows, but chart is drawing again like this: Actual behavior

Here is my code

import React from 'react'
import './Home.css'
import Graph from '../graph/Graph'
import MoreInfo from '../moreInfo/MoreInfo';

export default class Home extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            showMoreInfo: false
        };
    }

    changeState = () => {
        this.setState({
            showMoreInfo: !this.state.showMoreInfo
        });
    }

    render() {
        return (
            <div className='container'>
                <div className='more_info'>
                    {this.state.showMoreInfo && <MoreInfo />}
                </div>
                <div className='graphs'>
                    <Graph symbol='GOOGL' changeState={this.changeState} />
                </div>
            </div>
        )
    }
}

Code of Graph component

import React from 'react'
import './Graph.css'
import { createChart } from 'lightweight-charts'
import addToObserved from '../../images/addToObserved.svg'

function Graph(props) {
    const myRef = React.useRef();
    React.useEffect(() => {
        getData(props.symbol, 9, myRef);
    });

    return (
        <div className='graph-container'>
            <div ref={myRef} className='graph' />
            <button onClick={props.changeShowMoreInfoState}>More Info</button>
            <div className='addToObserved'>
                <button>
                    <p className='addToObserved-text'>Add to observed</p>
                    <img className='addToObserved-icon'
                        alt='observed_icon'
                        src={addToObserved} />
                </button>
            </div>
        </div>
    );
}

function getData(symbol, months, myRef) {
    let data = [{ time: '2020-10-14', value: 140 }];
    let to = new Date();
    let from = new Date();
    from.setMonth(from.getMonth() - months);
    request(`https://finnhub.io/api/v1/stock/candle` +
        `?symbol=${symbol}` +
        `&resolution=D` +
        `&from=${Math.round(from.getTime() / 1000)}` +
        `&to=${Math.round(to.getTime() / 1000)}` +
        `&token=***`,
        { json: true },
        function (error, response, body) {
            if (error) {
                console.log(error);
                return;
            }
            let date = from;
            body['o'].forEach((element) => {
                date.setDate(date.getDate() + 1);
                data.push({ time: getFormattedDate(date), value: element });
            });
            data.shift();
            makeChart(symbol, data, myRef);
        });
}

function makeChart(title, data, myRef) {
    let width = 550;
    let height = 300;
    let chart = window.tvchart = createChart(myRef.current, {
        width: width,
        height: height,
    });

    let series = chart.addAreaSeries();
    series.setData(data);
}

export default Graph;

I've already checked even without MoreInfo component. If I just change state of parent Component, graph draws one more time. If I click again on button, it draws third time and so more. If I display a lot of charts, all of them draw one more time every button click.


Solution

  • As @SureshKumar said, the problem was that I create new chart every time I get data in getData(). I fixed it by passing id to each chart and save it in map like this:

    <Graph
        id={1}symbol='GOOGL'
        changeShowMoreInfoState={this.changeShowMoreInfoState} />
    

    and changed my makeChart() method to this:

    let listOfCharts = new Map();
    
    function makeChart(id, title, data, myRef) {
        let width = 550;
        let height = 300;
        let chart = listOfCharts.get(id);
        if (chart === undefined) {
            chart = window.tvchart = createChart(myRef.current, {
                width: width,
                height: height,
                grid: {
                    horzLines: {
                        visible: false,
                    },
                },
            });
        } else {
            return;
        }
        ...
    }