#929·chartist

.detach() not removing the window resize listener.

Author: Kelnor277Created Apr 6, 2017Updated Mar 20, 2024

I have this react component

javascript
class ChartCell extends React.Component{
    constructor(props, context) {
        super(props, context);
        this.chartCreated = this.chartCreated.bind(this);
    }
    componentDidMount(){
        let mergedOptions = this.props.chartOptions;
        console.log('mounted');
        mergedOptions = _.assign(mergedOptions, defaultOptions);
        this.chartObj = new Chartist.Line(this.refs.chart, this.props.chartData, mergedOptions);
        this.chartObj.on('created', this.chartCreated); // Tried to call detach on "created" doesn't work
        this.chartObj.on('initial', this.chartCreated); // Neither does calling on "initial"
    }
    chartCreated(){
        console.log('detached');
        this.chartObj.detach(); //doesn't remove the event listener
        window.removeEventListener('resize', this.chartObj.resizeListener); //successfully removes the listener
    }
    render(){
        return (<td><div ref="chart" className="chart-div"/></td>)
    }
}

It does not clear the resize event listener. Which causes my page to lock up while redrawing every chart on a resize. I'm using a static sized div to contain the charts so I don't need to resize the charts on window redraw.

Investigation: So base.detach() will only not remove the listener if timeout is not set:

javascript
function detach() {
    // Only detach if initialization already occurred on this chart. If this chart still hasn't initialized (therefore
    // the initializationTimeoutId is still a valid timeout reference, we will clear the timeout
    if(!this.initializeTimeoutId) {
      window.removeEventListener('resize', this.resizeListener);
      this.optionsProvider.removeMediaQueryListeners();
    } else {
      window.clearTimeout(this.initializeTimeoutId);
    }

    return this;
  }

My guess here is base.initialize is the problem:

javascript
  function initialize() {
    // Add window resize listener that re-creates the chart
    window.addEventListener('resize', this.resizeListener);

    // Obtain current options based on matching media queries (if responsive options are given)
    // This will also register a listener that is re-creating the chart based on media changes
    this.optionsProvider = Chartist.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter);
    // Register options change listener that will trigger a chart update
    this.eventEmitter.addEventHandler('optionsChanged', function() {
      this.update();
    }.bind(this));

    // Before the first chart creation we need to register us with all plugins that are configured
    // Initialize all relevant plugins with our chart object and the plugin options specified in the config
    if(this.options.plugins) {
      this.options.plugins.forEach(function(plugin) {
        if(plugin instanceof Array) {
          plugin[0](this, plugin[1]);
        } else {
          plugin(this);
        }
      }.bind(this));
    }

    // Event for data transformation that allows to manipulate the data before it gets rendered in the charts
    this.eventEmitter.emit('data', {
      type: 'initial',
      data: this.data
    });

    // Create the first chart
    this.createChart(this.optionsProvider.getCurrentOptions());

    // As chart is initialized from the event loop now we can reset our timeout reference
    // This is important if the chart gets initialized on the same element twice
    this.initializeTimeoutId = undefined;
  }

this.initializeTimeoutId = undefined; is cleared AFTER the 'initial' event is emitted. That clearly shows why trying to detach() after 'initial' doesn't work. As for it not working on 'created" I don't know the lifecycle but maybe there's a race condition where initial fires, which causes created to fire later but before the initialize() function finishes and clears the timeout? So my question is A: should initializeTimeoutId be cleared before emitting the 'initial' event. OR B: which event should I wait for before detaching?