فهرست منبع

Perf on query field and typeahead

- Lower debounce
- refactor suggestion selection
- make Portal pure
- use prev suggestions object if suggestions are the same
David Kaltschmidt 7 سال پیش
والد
کامیت
538ea1127e

+ 1 - 0
public/app/features/explore/PromQueryField.tsx

@@ -575,6 +575,7 @@ class PromQueryField extends React.Component<PromQueryFieldProps, PromQueryField
               onWillApplySuggestion={willApplySuggestion}
               onValueChanged={this.onChangeQuery}
               placeholder="Enter a PromQL query"
+              portalPrefix="prometheus"
             />
           </div>
           {error ? <div className="prom-query-field-info text-error">{error}</div> : null}

+ 30 - 24
public/app/features/explore/QueryField.tsx

@@ -11,10 +11,17 @@ import NewlinePlugin from './slate-plugins/newline';
 import Typeahead from './Typeahead';
 import { makeFragment, makeValue } from './Value';
 
-export const TYPEAHEAD_DEBOUNCE = 300;
+export const TYPEAHEAD_DEBOUNCE = 100;
 
-function flattenSuggestions(s: any[]): any[] {
-  return s ? s.reduce((acc, g) => acc.concat(g.items), []) : [];
+function getSuggestionByIndex(suggestions: SuggestionGroup[], index: number): Suggestion {
+  // Flatten suggestion groups
+  const flattenedSuggestions = suggestions.reduce((acc, g) => acc.concat(g.items), []);
+  const correctedIndex = Math.max(index, 0) % flattenedSuggestions.length;
+  return flattenedSuggestions[correctedIndex];
+}
+
+function hasSuggestions(suggestions: SuggestionGroup[]): boolean {
+  return suggestions && suggestions.length > 0;
 }
 
 export interface Suggestion {
@@ -154,8 +161,14 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
     clearTimeout(this.resetTimer);
   }
 
-  componentDidUpdate() {
-    this.updateMenu();
+  componentDidUpdate(prevProps, prevState) {
+    // Only update menu location when suggestion existence or text/selection changed
+    if (
+      this.state.value !== prevState.value ||
+      hasSuggestions(this.state.suggestions) !== hasSuggestions(prevState.suggestions)
+    ) {
+      this.updateMenu();
+    }
   }
 
   componentWillReceiveProps(nextProps) {
@@ -216,7 +229,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
         wrapperNode,
       });
 
-      const filteredSuggestions = suggestions
+      let filteredSuggestions = suggestions
         .map(group => {
           if (group.items) {
             if (prefix) {
@@ -241,6 +254,11 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
         })
         .filter(group => group.items && group.items.length > 0); // Filter out empty groups
 
+      // Keep same object for equality checking later
+      if (_.isEqual(filteredSuggestions, this.state.suggestions)) {
+        filteredSuggestions = this.state.suggestions;
+      }
+
       this.setState(
         {
           suggestions: filteredSuggestions,
@@ -326,12 +344,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
             return undefined;
           }
 
-          // Get the currently selected suggestion
-          const flattenedSuggestions = flattenSuggestions(suggestions);
-          const selected = Math.abs(typeaheadIndex);
-          const selectedIndex = selected % flattenedSuggestions.length || 0;
-          const suggestion = flattenedSuggestions[selectedIndex];
-
+          const suggestion = getSuggestionByIndex(suggestions, typeaheadIndex);
           this.applyTypeahead(change, suggestion);
           return true;
         }
@@ -408,8 +421,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
     }
 
     // No suggestions or blur, remove menu
-    const hasSuggesstions = suggestions && suggestions.length > 0;
-    if (!hasSuggesstions) {
+    if (!hasSuggestions(suggestions)) {
       menu.removeAttribute('style');
       return;
     }
@@ -436,18 +448,12 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
 
   renderMenu = () => {
     const { portalPrefix } = this.props;
-    const { suggestions } = this.state;
-    const hasSuggesstions = suggestions && suggestions.length > 0;
-    if (!hasSuggesstions) {
+    const { suggestions, typeaheadIndex } = this.state;
+    if (!hasSuggestions(suggestions)) {
       return null;
     }
 
-    // Guard selectedIndex to be within the length of the suggestions
-    let selectedIndex = Math.max(this.state.typeaheadIndex, 0);
-    const flattenedSuggestions = flattenSuggestions(suggestions);
-    selectedIndex = selectedIndex % flattenedSuggestions.length || 0;
-    const selectedItem: Suggestion | null =
-      flattenedSuggestions.length > 0 ? flattenedSuggestions[selectedIndex] : null;
+    const selectedItem = getSuggestionByIndex(suggestions, typeaheadIndex);
 
     // Create typeahead in DOM root so we can later position it absolutely
     return (
@@ -482,7 +488,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
   }
 }
 
-class Portal extends React.Component<{ index?: number; prefix: string }, {}> {
+class Portal extends React.PureComponent<{ index?: number; prefix: string }, {}> {
   node: HTMLElement;
 
   constructor(props) {

+ 3 - 1
public/app/features/explore/Typeahead.tsx

@@ -23,7 +23,9 @@ class TypeaheadItem extends React.PureComponent<TypeaheadItemProps, {}> {
 
   componentDidUpdate(prevProps) {
     if (this.props.isSelected && !prevProps.isSelected) {
-      scrollIntoView(this.el);
+      requestAnimationFrame(() => {
+        scrollIntoView(this.el);
+      });
     }
   }