Perf on query field and typeahead
- Lower debounce - refactor suggestion selection - make Portal pure - use prev suggestions object if suggestions are the same
This commit is contained in:
@@ -575,6 +575,7 @@ class PromQueryField extends React.Component<PromQueryFieldProps, PromQueryField
|
|||||||
onWillApplySuggestion={willApplySuggestion}
|
onWillApplySuggestion={willApplySuggestion}
|
||||||
onValueChanged={this.onChangeQuery}
|
onValueChanged={this.onChangeQuery}
|
||||||
placeholder="Enter a PromQL query"
|
placeholder="Enter a PromQL query"
|
||||||
|
portalPrefix="prometheus"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{error ? <div className="prom-query-field-info text-error">{error}</div> : null}
|
{error ? <div className="prom-query-field-info text-error">{error}</div> : null}
|
||||||
|
|||||||
@@ -11,10 +11,17 @@ import NewlinePlugin from './slate-plugins/newline';
|
|||||||
import Typeahead from './Typeahead';
|
import Typeahead from './Typeahead';
|
||||||
import { makeFragment, makeValue } from './Value';
|
import { makeFragment, makeValue } from './Value';
|
||||||
|
|
||||||
export const TYPEAHEAD_DEBOUNCE = 300;
|
export const TYPEAHEAD_DEBOUNCE = 100;
|
||||||
|
|
||||||
function flattenSuggestions(s: any[]): any[] {
|
function getSuggestionByIndex(suggestions: SuggestionGroup[], index: number): Suggestion {
|
||||||
return s ? s.reduce((acc, g) => acc.concat(g.items), []) : [];
|
// 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 {
|
export interface Suggestion {
|
||||||
@@ -154,8 +161,14 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
|
|||||||
clearTimeout(this.resetTimer);
|
clearTimeout(this.resetTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
this.updateMenu();
|
// 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) {
|
componentWillReceiveProps(nextProps) {
|
||||||
@@ -216,7 +229,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
|
|||||||
wrapperNode,
|
wrapperNode,
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredSuggestions = suggestions
|
let filteredSuggestions = suggestions
|
||||||
.map(group => {
|
.map(group => {
|
||||||
if (group.items) {
|
if (group.items) {
|
||||||
if (prefix) {
|
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
|
.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(
|
this.setState(
|
||||||
{
|
{
|
||||||
suggestions: filteredSuggestions,
|
suggestions: filteredSuggestions,
|
||||||
@@ -326,12 +344,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the currently selected suggestion
|
const suggestion = getSuggestionByIndex(suggestions, typeaheadIndex);
|
||||||
const flattenedSuggestions = flattenSuggestions(suggestions);
|
|
||||||
const selected = Math.abs(typeaheadIndex);
|
|
||||||
const selectedIndex = selected % flattenedSuggestions.length || 0;
|
|
||||||
const suggestion = flattenedSuggestions[selectedIndex];
|
|
||||||
|
|
||||||
this.applyTypeahead(change, suggestion);
|
this.applyTypeahead(change, suggestion);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -408,8 +421,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No suggestions or blur, remove menu
|
// No suggestions or blur, remove menu
|
||||||
const hasSuggesstions = suggestions && suggestions.length > 0;
|
if (!hasSuggestions(suggestions)) {
|
||||||
if (!hasSuggesstions) {
|
|
||||||
menu.removeAttribute('style');
|
menu.removeAttribute('style');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -436,18 +448,12 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
|
|||||||
|
|
||||||
renderMenu = () => {
|
renderMenu = () => {
|
||||||
const { portalPrefix } = this.props;
|
const { portalPrefix } = this.props;
|
||||||
const { suggestions } = this.state;
|
const { suggestions, typeaheadIndex } = this.state;
|
||||||
const hasSuggesstions = suggestions && suggestions.length > 0;
|
if (!hasSuggestions(suggestions)) {
|
||||||
if (!hasSuggesstions) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guard selectedIndex to be within the length of the suggestions
|
const selectedItem = getSuggestionByIndex(suggestions, typeaheadIndex);
|
||||||
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;
|
|
||||||
|
|
||||||
// Create typeahead in DOM root so we can later position it absolutely
|
// Create typeahead in DOM root so we can later position it absolutely
|
||||||
return (
|
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;
|
node: HTMLElement;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ class TypeaheadItem extends React.PureComponent<TypeaheadItemProps, {}> {
|
|||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (this.props.isSelected && !prevProps.isSelected) {
|
if (this.props.isSelected && !prevProps.isSelected) {
|
||||||
scrollIntoView(this.el);
|
requestAnimationFrame(() => {
|
||||||
|
scrollIntoView(this.el);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user