Merge pull request #15113 from grafana/fix/explore-set-state-on-unmounted

Fix setState on unmounted component in Loki and Prometheus QueryField
This commit is contained in:
Torkel Ödegaard
2019-01-29 14:42:35 +01:00
committed by GitHub
3 changed files with 55 additions and 6 deletions
@@ -0,0 +1,22 @@
// https://github.com/facebook/react/issues/5465
export interface CancelablePromise<T> {
promise: Promise<T>;
cancel: () => void;
}
export const makePromiseCancelable = <T>(promise: Promise<T>): CancelablePromise<T> => {
let hasCanceled_ = false;
const wrappedPromise = new Promise<T>((resolve, reject) => {
promise.then(val => (hasCanceled_ ? reject({ isCanceled: true }) : resolve(val)));
promise.catch(error => (hasCanceled_ ? reject({ isCanceled: true }) : reject(error)));
});
return {
promise: wrappedPromise,
cancel() {
hasCanceled_ = true;
},
};
};
@@ -16,6 +16,7 @@ import RunnerPlugin from 'app/features/explore/slate-plugins/runner';
// Types
import { LokiQuery } from '../types';
import { TypeaheadOutput } from 'app/types/explore';
import { makePromiseCancelable, CancelablePromise } from 'app/core/utils/CancelablePromise';
const PRISM_SYNTAX = 'promql';
@@ -85,6 +86,7 @@ class LokiQueryField extends React.PureComponent<LokiQueryFieldProps, LokiQueryF
languageProvider: any;
modifiedSearch: string;
modifiedQuery: string;
languageProviderInitializationPromise: CancelablePromise<any>;
constructor(props: LokiQueryFieldProps, context) {
super(props, context);
@@ -112,12 +114,24 @@ class LokiQueryField extends React.PureComponent<LokiQueryFieldProps, LokiQueryF
componentDidMount() {
if (this.languageProvider) {
this.languageProvider
.start()
this.languageProviderInitializationPromise = makePromiseCancelable(this.languageProvider.start());
this.languageProviderInitializationPromise.promise
.then(remaining => {
remaining.map(task => task.then(this.onUpdateLanguage).catch(() => {}));
})
.then(() => this.onUpdateLanguage());
.then(() => this.onUpdateLanguage())
.catch(({ isCanceled }) => {
if (isCanceled) {
console.warn('LokiQueryField has unmounted, language provider intialization was canceled');
}
});
}
}
componentWillUnmount() {
if (this.languageProviderInitializationPromise) {
this.languageProviderInitializationPromise.cancel();
}
}
@@ -12,6 +12,7 @@ import BracesPlugin from 'app/features/explore/slate-plugins/braces';
import RunnerPlugin from 'app/features/explore/slate-plugins/runner';
import QueryField, { TypeaheadInput, QueryFieldState } from 'app/features/explore/QueryField';
import { PromQuery } from '../types';
import { CancelablePromise, makePromiseCancelable } from 'app/core/utils/CancelablePromise';
const HISTOGRAM_GROUP = '__histograms__';
const METRIC_MARK = 'metric';
@@ -104,6 +105,7 @@ interface PromQueryFieldState {
class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryFieldState> {
plugins: any[];
languageProvider: any;
languageProviderInitializationPromise: CancelablePromise<any>;
constructor(props: PromQueryFieldProps, context) {
super(props, context);
@@ -129,12 +131,23 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
componentDidMount() {
if (this.languageProvider) {
this.languageProvider
.start()
this.languageProviderInitializationPromise = makePromiseCancelable(this.languageProvider.start());
this.languageProviderInitializationPromise.promise
.then(remaining => {
remaining.map(task => task.then(this.onUpdateLanguage).catch(() => {}));
})
.then(() => this.onUpdateLanguage());
.then(() => this.onUpdateLanguage())
.catch(({ isCanceled }) => {
if (isCanceled) {
console.warn('PromQueryField has unmounted, language provider intialization was canceled');
}
});
}
}
componentWillUnmount() {
if (this.languageProviderInitializationPromise) {
this.languageProviderInitializationPromise.cancel();
}
}