From 9dc8e02b1d8a3baadc6c8679634ad50a055f1254 Mon Sep 17 00:00:00 2001 From: Will Piers <willpiers@influxdb.com> Date: Thu, 1 Dec 2016 16:31:21 -0800 Subject: [PATCH] Refactor HostsTable for fun and SPEED --- ui/src/hosts/components/HostsTable.js | 144 ++++++++++++++++---------- 1 file changed, 89 insertions(+), 55 deletions(-) diff --git a/ui/src/hosts/components/HostsTable.js b/ui/src/hosts/components/HostsTable.js index c93cfad89..c866952b6 100644 --- a/ui/src/hosts/components/HostsTable.js +++ b/ui/src/hosts/components/HostsTable.js @@ -19,24 +19,14 @@ const HostsTable = React.createClass({ getInitialState() { return { searchTerm: '', - filteredHosts: this.props.hosts, sortDirection: null, sortKey: null, }; }, - componentWillReceiveProps(newProps) { - this.filterHosts(newProps.hosts, this.state.searchTerm); - }, - - filterHosts(allHosts, searchTerm) { - const hosts = allHosts.filter((h) => { - let apps = null; - if (h.apps) { - apps = h.apps.join(', '); - } else { - apps = ''; - } + filter(allHosts, searchTerm) { + return allHosts.filter((h) => { + const apps = h.apps ? h.apps.join(', ') : ''; // search each tag for the presence of the search term let tagResult = false; if (h.tags) { @@ -52,17 +42,6 @@ const HostsTable = React.createClass({ tagResult ); }); - this.setState({searchTerm, filteredHosts: hosts}); - }, - - changeSort(key) { - // if we're using the key, reverse order; otherwise, set it with ascending - if (this.state.sortKey === key) { - const reverseDirection = (this.state.sortDirection === 'asc' ? 'desc' : 'asc'); - this.setState({sortDirection: reverseDirection}); - } else { - this.setState({sortKey: key, sortDirection: 'asc'}); - } }, sort(hosts, key, direction) { @@ -76,6 +55,20 @@ const HostsTable = React.createClass({ } }, + updateSearchTerm(term) { + this.setState({searchTerm: term}); + }, + + updateSort(key) { + // if we're using the key, reverse order; otherwise, set it with ascending + if (this.state.sortKey === key) { + const reverseDirection = (this.state.sortDirection === 'asc' ? 'desc' : 'asc'); + this.setState({sortDirection: reverseDirection}); + } else { + this.setState({sortKey: key, sortDirection: 'asc'}); + } + }, + sortableClasses(key) { if (this.state.sortKey === key) { if (this.state.sortDirection === 'asc') { @@ -87,52 +80,32 @@ const HostsTable = React.createClass({ }, render() { - const hosts = this.sort(this.state.filteredHosts, this.state.sortKey, this.state.sortDirection); - const hostCount = hosts.length; - const {source} = this.props; + const {searchTerm, sortKey, sortDirection} = this.state; + const {hosts, source} = this.props; + const sortedHosts = this.sort(this.filter(hosts, searchTerm), sortKey, sortDirection); + const hostCount = sortedHosts.length; return ( <div className="panel panel-minimal"> <div className="panel-heading u-flex u-ai-center u-jc-space-between"> <h2 className="panel-title">{hostCount ? `${hostCount} Hosts` : ''}</h2> - <SearchBar onSearch={_.wrap(this.props.hosts, this.filterHosts)} /> + <SearchBar onSearch={this.updateSearchTerm} /> </div> <div className="panel-body"> <table className="table v-center"> <thead> <tr> - <th onClick={() => this.changeSort('name')} className={this.sortableClasses('name')}>Hostname</th> + <th onClick={() => this.updateSort('name')} className={this.sortableClasses('name')}>Hostname</th> <th className="text-center">Status</th> - <th onClick={() => this.changeSort('cpu')} className={this.sortableClasses('cpu')}>CPU</th> - <th onClick={() => this.changeSort('load')} className={this.sortableClasses('load')}>Load</th> + <th onClick={() => this.updateSort('cpu')} className={this.sortableClasses('cpu')}>CPU</th> + <th onClick={() => this.updateSort('load')} className={this.sortableClasses('load')}>Load</th> <th>Apps</th> </tr> </thead> <tbody> { - hosts.map(({name, cpu, load, apps = []}) => { - return ( - <tr key={name}> - <td className="monotype"><Link to={`/sources/${source.id}/hosts/${name}`}>{name}</Link></td> - <td className="text-center"><div className="table-dot dot-success"></div></td> - <td className="monotype">{isNaN(cpu) ? 'N/A' : `${cpu.toFixed(2)}%`}</td> - <td className="monotype">{isNaN(load) ? 'N/A' : `${load.toFixed(2)}`}</td> - <td className="monotype"> - {apps.map((app, index) => { - return ( - <span key={app}> - <Link - style={{marginLeft: "2px"}} - to={{pathname: `/sources/${source.id}/hosts/${name}`, query: {app}}}> - {app} - </Link> - {index === apps.length - 1 ? null : ', '} - </span> - ); - })} - </td> - </tr> - ); + sortedHosts.map((h) => { + return <HostRow key={h.name} host={h} source={source} />; }) } </tbody> @@ -143,13 +116,74 @@ const HostsTable = React.createClass({ }, }); +const HostRow = React.createClass({ + propTypes: { + source: PropTypes.shape({ + id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + }).isRequired, + host: PropTypes.shape({ + name: PropTypes.string, + cpu: PropTypes.number, + load: PropTypes.number, + apps: PropTypes.arrayOf(PropTypes.string.isRequired), + }), + }, + + shouldComponentUpdate(nextProps) { + return this.props.host !== nextProps.host; + }, + + render() { + const {host, source} = this.props; + const {name, cpu, load, apps = []} = host; + return ( + <tr> + <td className="monotype"><Link to={`/sources/${source.id}/hosts/${name}`}>{name}</Link></td> + <td className="text-center"><div className="table-dot dot-success"></div></td> + <td className="monotype">{isNaN(cpu) ? 'N/A' : `${cpu.toFixed(2)}%`}</td> + <td className="monotype">{isNaN(load) ? 'N/A' : `${load.toFixed(2)}`}</td> + <td className="monotype"> + {apps.map((app, index) => { + return ( + <span key={app}> + <Link + style={{marginLeft: "2px"}} + to={{pathname: `/sources/${source.id}/hosts/${name}`, query: {app}}}> + {app} + </Link> + {index === apps.length - 1 ? null : ', '} + </span> + ); + })} + </td> + </tr> + ); + }, +}); + const SearchBar = React.createClass({ propTypes: { onSearch: PropTypes.func.isRequired, }, + getInitialState() { + return { + searchTerm: '', + }; + }, + + componentWillMount() { + const waitPeriod = 300; + this.handleSearch = _.debounce(this.handleSearch, waitPeriod); + }, + + handleSearch() { + this.props.onSearch(this.state.searchTerm); + }, + handleChange() { - this.props.onSearch(this.refs.searchInput.value); + this.setState({searchTerm: this.refs.searchInput.value}, this.handleSearch); }, render() {