diff --git a/packages/grafana-data/src/panel/PanelPlugin.ts b/packages/grafana-data/src/panel/PanelPlugin.ts
index dfbb009efdc..b4e56fb52d6 100644
--- a/packages/grafana-data/src/panel/PanelPlugin.ts
+++ b/packages/grafana-data/src/panel/PanelPlugin.ts
@@ -375,4 +375,8 @@ export class PanelPlugin<
getSuggestionsSupplier(): VisualizationSuggestionsSupplier | undefined {
return this.suggestionsSupplier;
}
+
+ hasPluginId(pluginId: string) {
+ return this.meta.id === pluginId;
+ }
}
diff --git a/packages/grafana-runtime/src/components/PanelRenderer.tsx b/packages/grafana-runtime/src/components/PanelRenderer.tsx
index 374ac9bb5fe..8c0619ba3ba 100644
--- a/packages/grafana-runtime/src/components/PanelRenderer.tsx
+++ b/packages/grafana-runtime/src/components/PanelRenderer.tsx
@@ -16,6 +16,7 @@ export interface PanelRendererProps
;
onOptionsChange?: (options: P) => void;
+ onFieldConfigChange?: (config: FieldConfigSource) => void;
onChangeTimeRange?: (timeRange: AbsoluteTimeRange) => void;
fieldConfig?: FieldConfigSource>;
timeZone?: string;
diff --git a/public/app/features/panel/components/PanelRenderer.tsx b/public/app/features/panel/components/PanelRenderer.tsx
index 98a86b3a209..2c0a9a55aee 100644
--- a/public/app/features/panel/components/PanelRenderer.tsx
+++ b/public/app/features/panel/components/PanelRenderer.tsx
@@ -1,5 +1,4 @@
import React, { useState, useMemo, useEffect, useRef } from 'react';
-import { useAsync } from 'react-use';
import { applyFieldOverrides, FieldConfigSource, getTimeZone, PanelData, PanelPlugin } from '@grafana/data';
import { PanelRendererProps } from '@grafana/runtime';
@@ -7,7 +6,7 @@ import { ErrorBoundaryAlert, useTheme2 } from '@grafana/ui';
import { appEvents } from 'app/core/core';
import { getPanelOptionsWithDefaults, OptionDefaults } from '../../dashboard/state/getPanelOptionsWithDefaults';
-import { importPanelPlugin } from '../../plugins/importPanelPlugin';
+import { importPanelPlugin, syncGetPanelPlugin } from '../../plugins/importPanelPlugin';
const defaultFieldConfig = { defaults: {}, overrides: [] };
@@ -22,27 +21,38 @@ export function PanelRenderer(pr
title,
onOptionsChange = () => {},
onChangeTimeRange = () => {},
- fieldConfig: externalFieldConfig = defaultFieldConfig,
+ onFieldConfigChange = () => {},
+ fieldConfig = defaultFieldConfig,
} = props;
- const [localFieldConfig, setFieldConfig] = useState(externalFieldConfig);
- const { value: plugin, error, loading } = useAsync(() => importPanelPlugin(pluginId), [pluginId]);
- const optionsWithDefaults = useOptionDefaults(plugin, options, localFieldConfig);
+ const [plugin, setPlugin] = useState(syncGetPanelPlugin(pluginId));
+ const [error, setError] = useState();
+ const optionsWithDefaults = useOptionDefaults(plugin, options, fieldConfig);
const dataWithOverrides = useFieldOverrides(plugin, optionsWithDefaults, data, timeZone);
useEffect(() => {
- setFieldConfig((lfc) => ({ ...lfc, ...externalFieldConfig }));
- }, [externalFieldConfig]);
+ // If we already have a plugin and it's correct one do nothing
+ if (plugin && plugin.hasPluginId(pluginId)) {
+ return;
+ }
+
+ // Async load the plugin
+ importPanelPlugin(pluginId)
+ .then((result) => setPlugin(result))
+ .catch((err: Error) => {
+ setError(err.message);
+ });
+ }, [pluginId, plugin]);
if (error) {
- return Failed to load plugin: {error.message}
;
+ return Failed to load plugin: {error}
;
}
- if (pluginIsLoading(loading, plugin, pluginId)) {
+ if (!plugin || !plugin.hasPluginId(pluginId)) {
return Loading plugin panel...
;
}
- if (!plugin || !plugin.panel) {
+ if (!plugin.panel) {
return Seems like the plugin you are trying to load does not have a panel component.
;
}
@@ -61,14 +71,14 @@ export function PanelRenderer(pr
timeRange={dataWithOverrides.timeRange}
timeZone={timeZone}
options={optionsWithDefaults!.options}
- fieldConfig={localFieldConfig}
+ fieldConfig={fieldConfig}
transparent={false}
width={width}
height={height}
renderCounter={0}
replaceVariables={(str: string) => str}
onOptionsChange={onOptionsChange}
- onFieldConfigChange={setFieldConfig}
+ onFieldConfigChange={onFieldConfigChange}
onChangeTimeRange={onChangeTimeRange}
eventBus={appEvents}
/>
@@ -127,7 +137,3 @@ function useFieldOverrides(
};
}, [fieldConfigRegistry, fieldConfig, data, series, timeZone, theme]);
}
-
-function pluginIsLoading(loading: boolean, plugin: PanelPlugin | undefined, pluginId: string) {
- return loading || plugin?.meta.id !== pluginId;
-}
diff --git a/public/app/features/plugins/importPanelPlugin.ts b/public/app/features/plugins/importPanelPlugin.ts
index 042db64bae1..b6994de5fb7 100644
--- a/public/app/features/plugins/importPanelPlugin.ts
+++ b/public/app/features/plugins/importPanelPlugin.ts
@@ -1,16 +1,15 @@
-import * as grafanaData from '@grafana/data';
+import { PanelPlugin, PanelPluginMeta } from '@grafana/data';
import config from 'app/core/config';
import { getPanelPluginLoadError } from '../panel/components/PanelPluginError';
import { importPluginModule } from './plugin_loader';
-interface PanelCache {
- [key: string]: Promise;
-}
-const panelCache: PanelCache = {};
-export function importPanelPlugin(id: string): Promise {
- const loaded = panelCache[id];
+const promiseCache: Record> = {};
+const panelPluginCache: Record = {};
+
+export function importPanelPlugin(id: string): Promise {
+ const loaded = promiseCache[id];
if (loaded) {
return loaded;
}
@@ -21,22 +20,26 @@ export function importPanelPlugin(id: string): Promise
throw new Error(`Plugin ${id} not found`);
}
- panelCache[id] = getPanelPlugin(meta);
+ promiseCache[id] = getPanelPlugin(meta);
- return panelCache[id];
+ return promiseCache[id];
}
-export function importPanelPluginFromMeta(meta: grafanaData.PanelPluginMeta): Promise {
+export function importPanelPluginFromMeta(meta: PanelPluginMeta): Promise {
return getPanelPlugin(meta);
}
-function getPanelPlugin(meta: grafanaData.PanelPluginMeta): Promise {
+export function syncGetPanelPlugin(id: string): PanelPlugin | undefined {
+ return panelPluginCache[id];
+}
+
+function getPanelPlugin(meta: PanelPluginMeta): Promise {
return importPluginModule(meta.module, meta.info?.version)
.then((pluginExports) => {
if (pluginExports.plugin) {
- return pluginExports.plugin as grafanaData.PanelPlugin;
+ return pluginExports.plugin as PanelPlugin;
} else if (pluginExports.PanelCtrl) {
- const plugin = new grafanaData.PanelPlugin(null);
+ const plugin = new PanelPlugin(null);
plugin.angularPanelCtrl = pluginExports.PanelCtrl;
return plugin;
}
@@ -44,6 +47,7 @@ function getPanelPlugin(meta: grafanaData.PanelPluginMeta): Promise {
plugin.meta = meta;
+ panelPluginCache[meta.id] = plugin;
return plugin;
})
.catch((err) => {