_handleEvent(e, replay, inChartArea) {
    const {_active: lastActive = [], options} = this;

    // If the event is replayed from `update`, we should evaluate with the final positions.
    //
    // The `replay`:
    // It's the last event (excluding click) that has occurred before `update`.
    // So mouse has not moved. It's also over the chart, because there is a `replay`.
    //
    // The why:
    // If animations are active, the elements haven't moved yet compared to state before update.
    // But if they will, we are activating the elements that would be active, if this check
    // was done after the animations have completed. => "final positions".
    // If there is no animations, the "final" and "current" positions are equal.
    // This is done so we do not have to evaluate the active elements each animation frame
    // - it would be expensive.
    const useFinalPosition = replay;
    const active = this._getActiveElements(e, lastActive, inChartArea, useFinalPosition);
    const isClick = _isClickEvent(e);
    const lastEvent = determineLastEvent(e, this._lastEvent, inChartArea, isClick);

    if (inChartArea) {
      // Set _lastEvent to null while we are processing the event handlers.
      // This prevents recursion if the handler calls chart.update()
      this._lastEvent = null;

      // Invoke onHover hook
      callCallback(options.onHover, [e, active, this], this);

      if (isClick) {
        callCallback(options.onClick, [e, active, this], this);
      }
    }

    const changed = !_elementsEqual(active, lastActive);
    if (changed || replay) {
      this._active = active;
      this._updateHoverStyles(active, lastActive, replay);
    }

    this._lastEvent = lastEvent;

    return changed;
  }
Handle an event
@param {ChartEvent} e the event to handle
@param {boolean} [replay] - true if the event was replayed by `update`
@param {boolean} [inChartArea] - true if the event is inside chartArea
@return {boolean} true if the chart needs to re-render
@private