diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index 471581cb257262..7e95479887dbd1 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -8,9 +8,11 @@ import './app.scss'; import _ from 'lodash'; -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useRef } from 'react'; import { i18n } from '@kbn/i18n'; -import { NotificationsStart } from 'kibana/public'; +import { NotificationsStart, Toast } from 'kibana/public'; +import { VisualizeFieldContext } from 'src/plugins/ui_actions/public'; +import { Datatable } from 'src/plugins/expressions/public'; import { EuiBreadcrumb } from '@elastic/eui'; import { downloadMultipleAs } from '../../../../../src/plugins/share/public'; import { @@ -26,21 +28,27 @@ import { injectFilterReferences } from '../persistence'; import { NativeRenderer } from '../native_renderer'; import { trackUiEvent } from '../lens_ui_telemetry'; import { + DataPublicPluginStart, esFilters, exporters, + Filter, IndexPattern as IndexPatternInstance, IndexPatternsContract, + Query, + SavedQuery, syncQueryStateWithUrl, } from '../../../../../src/plugins/data/public'; import { LENS_EMBEDDABLE_TYPE, getFullPath } from '../../common'; import { LensAppProps, LensAppServices, LensAppState } from './types'; import { getLensTopNavConfig } from './lens_top_nav'; +import { Document } from '../persistence'; import { SaveModal } from './save_modal'; import { LensByReferenceInput, LensEmbeddableInput, } from '../editor_frame_service/embeddable/embeddable'; import { useTimeRange } from './time_range'; +import { EditorFrameInstance } from '../types'; export function App({ history, @@ -515,6 +523,12 @@ export function App({ } }; + const lastKnownDocRef = useRef(state.lastKnownDoc); + lastKnownDocRef.current = state.lastKnownDoc; + + const activeDataRef = useRef(state.activeData); + activeDataRef.current = state.activeData; + const { TopNavMenu } = navigation.ui; const savingPermitted = Boolean(state.isSaveable && application.capabilities.visualize.save); @@ -660,50 +674,24 @@ export function App({ /> {(!state.isLoading || state.persistedDoc) && ( - { - if (isSaveable !== state.isSaveable) { - setState((s) => ({ ...s, isSaveable })); - } - if (!_.isEqual(state.persistedDoc, doc) && !_.isEqual(state.lastKnownDoc, doc)) { - setState((s) => ({ ...s, lastKnownDoc: doc })); - } - if (!_.isEqual(state.activeData, activeData)) { - setState((s) => ({ ...s, activeData })); - } - - // Update the cached index patterns if the user made a change to any of them - if ( - state.indexPatternsForTopNav.length !== filterableIndexPatterns.length || - filterableIndexPatterns.some( - (id) => - !state.indexPatternsForTopNav.find((indexPattern) => indexPattern.id === id) - ) - ) { - getAllIndexPatterns( - filterableIndexPatterns, - data.indexPatterns, - notifications - ).then((indexPatterns) => { - if (indexPatterns) { - setState((s) => ({ ...s, indexPatternsForTopNav: indexPatterns })); - } - }); - } - }, - }} + )} @@ -732,6 +720,89 @@ export function App({ ); } +const MemoizedEditorFrameWrapper = React.memo(function EditorFrameWrapper({ + editorFrame, + query, + filters, + searchSessionId, + isSaveable: oldIsSaveable, + savedQuery, + persistedDoc, + indexPatterns: indexPatternsForTopNav, + resolvedDateRange, + onError, + showNoDataPopover, + initialContext, + setState, + data, + notifications, + lastKnownDoc, + activeData: activeDataRef, +}: { + editorFrame: EditorFrameInstance; + searchSessionId: string; + query: Query; + filters: Filter[]; + isSaveable: boolean; + savedQuery?: SavedQuery; + persistedDoc?: Document | undefined; + indexPatterns: IndexPatternInstance[]; + resolvedDateRange: { fromDate: string; toDate: string }; + onError: (e: { message: string }) => Toast; + showNoDataPopover: () => void; + initialContext: VisualizeFieldContext | undefined; + setState: React.Dispatch>; + data: DataPublicPluginStart; + notifications: NotificationsStart; + lastKnownDoc: React.MutableRefObject; + activeData: React.MutableRefObject | undefined>; +}) { + return ( + { + if (isSaveable !== oldIsSaveable) { + setState((s) => ({ ...s, isSaveable })); + } + if (!_.isEqual(persistedDoc, doc) && !_.isEqual(lastKnownDoc.current, doc)) { + setState((s) => ({ ...s, lastKnownDoc: doc })); + } + if (!_.isEqual(activeDataRef.current, activeData)) { + setState((s) => ({ ...s, activeData })); + } + + // Update the cached index patterns if the user made a change to any of them + if ( + indexPatternsForTopNav.length !== filterableIndexPatterns.length || + filterableIndexPatterns.some( + (id) => !indexPatternsForTopNav.find((indexPattern) => indexPattern.id === id) + ) + ) { + getAllIndexPatterns(filterableIndexPatterns, data.indexPatterns, notifications).then( + (indexPatterns) => { + if (indexPatterns) { + setState((s) => ({ ...s, indexPatternsForTopNav: indexPatterns })); + } + } + ); + } + }, + }} + /> + ); +}); + export async function getAllIndexPatterns( ids: string[], indexPatternsService: IndexPatternsContract,