improve perf of instance select dropdown

This commit is contained in:
Tao Bojlen 2018-08-29 03:52:21 +02:00
parent 74511c67dd
commit 0f2aa3c938
8 changed files with 37 additions and 35 deletions

View file

@ -1,10 +0,0 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { App } from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});

View file

@ -10,7 +10,7 @@ import { fetchInstances } from './redux/actions';
import { IAppState, IInstance } from './redux/types'; import { IAppState, IInstance } from './redux/types';
interface IAppProps { interface IAppProps {
currentInstance: IInstance | null; currentInstanceName?: string | null;
instances?: IInstance[], instances?: IInstance[],
isLoadingInstances: boolean, isLoadingInstances: boolean,
fetchInstances: () => void; fetchInstances: () => void;
@ -61,7 +61,7 @@ class AppImpl extends React.Component<IAppProps> {
className="fediverse-welcome" className="fediverse-welcome"
icon={IconNames.SEARCH_AROUND} icon={IconNames.SEARCH_AROUND}
title="Graph. TODO" title="Graph. TODO"
description={"Selected " + ((this.props.currentInstance && this.props.currentInstance.name) || "nothing")} description={"Selected " + (this.props.currentInstanceName || "nothing")}
/> />
</div> </div>
); );
@ -70,7 +70,7 @@ class AppImpl extends React.Component<IAppProps> {
} }
const mapStateToProps = (state: IAppState) => ({ const mapStateToProps = (state: IAppState) => ({
currentInstance: state.currentInstance, currentInstanceName: state.currentInstanceName,
instances: state.data.instances, instances: state.data.instances,
isLoadingInstances: state.data.isLoadingInstances, isLoadingInstances: state.data.isLoadingInstances,
}) })

View file

@ -1,17 +1,16 @@
import * as React from 'react'; import * as React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
// import { List, ListRowProps } from 'react-virtualized';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { Button, MenuItem } from '@blueprintjs/core'; import { Button, MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import { ItemPredicate, ItemRenderer, Select } from '@blueprintjs/select'; import { IItemRendererProps, ItemPredicate, Select } from '@blueprintjs/select';
import { selectInstance } from '../redux/actions'; import { selectInstance } from '../redux/actions';
import { IAppState, IInstance } from '../redux/types'; import { IAppState, IInstance } from '../redux/types';
interface IInstanceSearchProps { interface IInstanceSearchProps {
currentInstance: IInstance | null; currentInstanceName: string | null;
instances?: IInstance[]; instances?: IInstance[];
selectInstance: (instanceName: string) => void; selectInstance: (instanceName: string) => void;
} }
@ -28,33 +27,48 @@ class InstanceSearchImpl extends React.Component<IInstanceSearchProps> {
onItemSelect={this.onItemSelect} onItemSelect={this.onItemSelect}
itemPredicate={this.itemPredicate} itemPredicate={this.itemPredicate}
disabled={!this.props.instances} disabled={!this.props.instances}
initialContent={this.renderInitialContent()}
noResults={this.renderNoResults()}
popoverProps={{popoverClassName: "fediverse-instance-search-popover"}}
> >
<Button <Button
icon={IconNames.SELECTION} icon={IconNames.SELECTION}
rightIcon={IconNames.CARET_DOWN} rightIcon={IconNames.CARET_DOWN}
text={(this.props.currentInstance && this.props.currentInstance.name) || ("Select an instance")} text={this.props.currentInstanceName || ("Select an instance")}
disabled={!this.props.instances} disabled={!this.props.instances}
/> />
</InstanceSelect> </InstanceSelect>
); );
} }
private itemRenderer: ItemRenderer<IInstance> = (item, { handleClick, modifiers }) => { private renderInitialContent = () => {
if (!modifiers.matchesPredicate) { return (
<MenuItem disabled={true} text={"Start typing"} />
);
}
private renderNoResults = () => {
return (
<MenuItem disabled={true} text={"Keep typing"} />
);
}
private itemRenderer = (item: IInstance, itemProps: IItemRendererProps) => {
if (!itemProps.modifiers.matchesPredicate) {
return null; return null;
} }
return ( return (
<MenuItem <MenuItem
text={item.name} text={item.name}
key={item.name} key={item.name}
active={modifiers.active} active={itemProps.modifiers.active}
onClick={handleClick} onClick={itemProps.handleClick}
/> />
); );
} }
private itemPredicate: ItemPredicate<IInstance> = (query, item, index) => { private itemPredicate: ItemPredicate<IInstance> = (query, item, index) => {
if (!item.name) { if (!item.name || query.length < 4) {
return false; return false;
} }
return item.name.toLowerCase().indexOf(query.toLowerCase()) >= 0; return item.name.toLowerCase().indexOf(query.toLowerCase()) >= 0;
@ -66,7 +80,7 @@ class InstanceSearchImpl extends React.Component<IInstanceSearchProps> {
} }
const mapStateToProps = (state: IAppState) => ({ const mapStateToProps = (state: IAppState) => ({
currentInstance: state.currentInstance, currentInstanceName: state.currentInstanceName,
instances: state.data.instances, instances: state.data.instances,
}) })
const mapDispatchToProps = (dispatch: Dispatch) => ({ const mapDispatchToProps = (dispatch: Dispatch) => ({

View file

@ -12,7 +12,7 @@ html, body {
} }
.fediverse-instance-search-popover { .fediverse-instance-search-popover {
height: 350px; max-height: 350px;
min-width: 214px; min-width: 300px;
overflow-x: hidden; overflow-x: hidden;
} }

View file

@ -3,11 +3,9 @@ import { Dispatch } from 'redux';
import { getFromApi } from '../util'; import { getFromApi } from '../util';
import { ActionType, IInstance } from './types'; import { ActionType, IInstance } from './types';
export const selectInstance = (instance: string) => { export const selectInstance = (instanceName: string) => {
return { return {
payload: { payload: instanceName,
instance,
},
type: ActionType.SELECT_INSTANCE, type: ActionType.SELECT_INSTANCE,
} }
} }

View file

@ -1,6 +1,6 @@
import { combineReducers } from 'redux'; import { combineReducers } from 'redux';
import { ActionType, IAction, IDataState, IInstance } from './types'; import { ActionType, IAction, IDataState } from './types';
const initialDataState = { const initialDataState = {
isLoadingInstances: false, isLoadingInstances: false,
@ -24,7 +24,7 @@ const data = (state: IDataState = initialDataState, action: IAction) => {
} }
} }
const currentInstance = (state: IInstance | null = null, action: IAction): IInstance | null => { const currentInstanceName = (state: string | null = null, action: IAction): string | null => {
switch (action.type) { switch (action.type) {
case ActionType.SELECT_INSTANCE: case ActionType.SELECT_INSTANCE:
return action.payload; return action.payload;
@ -34,6 +34,6 @@ const currentInstance = (state: IInstance | null = null, action: IAction): IInst
} }
export const rootReducer = combineReducers({ export const rootReducer = combineReducers({
currentInstance, currentInstanceName,
data, data,
}) })

View file

@ -20,6 +20,6 @@ export interface IDataState {
} }
export interface IAppState { export interface IAppState {
currentInstance: IInstance | null, currentInstanceName: string | null,
data: IDataState, data: IDataState,
} }

View file

@ -1,7 +1,7 @@
import fetch from 'cross-fetch'; import fetch from 'cross-fetch';
// const API_ROOT = "https://fediverse.space/api/v1/" const API_ROOT = "https://fediverse.space/api/v1/"
const API_ROOT = "http://localhost:8000/api/v1/" // const API_ROOT = "http://localhost:8000/api/v1/"
export const getFromApi = (path: string): Promise<any> => { export const getFromApi = (path: string): Promise<any> => {
const domain = API_ROOT.endsWith("/") ? API_ROOT : API_ROOT + "/"; const domain = API_ROOT.endsWith("/") ? API_ROOT : API_ROOT + "/";