Browse Source

Make language provider cancelable in Loki and Prometheus QueryField, to avoid setting state on unmounted component

Dominik Prokop 7 years ago
parent
commit
27948b1308

+ 17 - 3
public/app/plugins/datasource/loki/components/LokiQueryField.tsx

@@ -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();
     }
   }
 

+ 16 - 3
public/app/plugins/datasource/prometheus/components/PromQueryField.tsx

@@ -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();
     }
   }