Don't sort the list on every render (#58)

This commit is contained in:
Maciej Hirsz
2018-09-27 17:01:51 +02:00
committed by GitHub
parent 87facf1552
commit 62d2847bb0
13 changed files with 416 additions and 67 deletions
+4 -3
View File
@@ -3,7 +3,7 @@ import { Types } from '@dotstats/common';
import { Chains, Chain, Ago, OfflineIndicator } from './components';
import { Connection } from './Connection';
import { PersistentObject, PersistentSet } from './persist';
import { State } from './state';
import { State, compareNodes } from './state';
import './App.css';
@@ -36,13 +36,13 @@ export default class App extends React.Component<{}, State> {
);
this.pins = new PersistentSet<Types.NodeName>('pinned_names', (pins) => {
const { nodes } = this.state;
const { nodes, sortedNodes } = this.state;
for (const node of nodes.values()) {
node.pinned = pins.has(node.nodeDetails[0]);
}
this.setState({ nodes, pins });
this.setState({ nodes, pins, sortedNodes: sortedNodes.sort(compareNodes) });
});
this.state = {
@@ -54,6 +54,7 @@ export default class App extends React.Component<{}, State> {
subscribed: null,
chains: new Map(),
nodes: new Map(),
sortedNodes: [],
settings: this.settings.raw(),
pins: this.pins.get(),
};
+30 -9
View File
@@ -1,5 +1,6 @@
import { VERSION, timestamp, FeedMessage, Types, Maybe, sleep } from '@dotstats/common';
import { State, Update } from './state';
import { sortedInsert, sortedIndexOf } from '@dotstats/common';
import { State, Update, compareNodes } from './state';
import { PersistentSet } from './persist';
const { Actions } = FeedMessage;
@@ -140,9 +141,8 @@ export class Connection {
private handleMessages = (event: MessageEvent) => {
const data = event.data as FeedMessage.Data;
const nodes = this.state.nodes;
const chains = this.state.chains;
const changes = { nodes, chains };
const { nodes, chains } = this.state;
let { sortedNodes } = this.state;
messages: for (const message of FeedMessage.deserialize(data)) {
switch (message.action) {
@@ -176,14 +176,32 @@ export class Connection {
const node = { pinned, id, nodeDetails, nodeStats, blockDetails, location };
nodes.set(id, node);
sortedInsert(node, sortedNodes, compareNodes);
if (nodes.size !== sortedNodes.length) {
console.error('Node count in sorted array is wrong!');
}
break;
}
case Actions.RemovedNode: {
nodes.delete(message.payload);
const id = message.payload;
const node = nodes.get(id);
break;
if (node) {
nodes.delete(id);
const index = sortedIndexOf(node, sortedNodes, compareNodes);
sortedNodes.splice(index, 1);
if (nodes.size !== sortedNodes.length) {
console.error('Node count in sorted array is wrong!');
}
break;
}
continue messages;
}
case Actions.LocatedNode: {
@@ -191,7 +209,7 @@ export class Connection {
const node = nodes.get(id);
if (!node) {
return;
continue messages;
}
node.location = [latitude, longitude, city];
@@ -204,10 +222,11 @@ export class Connection {
const node = nodes.get(id);
if (!node) {
return;
continue messages;
}
node.blockDetails = blockDetails;
sortedNodes = sortedNodes.sort(compareNodes);
break;
}
@@ -245,6 +264,7 @@ export class Connection {
if (this.state.subscribed === message.payload) {
nodes.clear();
sortedNodes = [];
this.state = this.update({ subscribed: null, nodes, chains });
@@ -263,6 +283,7 @@ export class Connection {
case Actions.UnsubscribedFrom: {
if (this.state.subscribed === message.payload) {
nodes.clear();
sortedNodes = [];
this.state = this.update({ subscribed: null, nodes });
}
@@ -281,7 +302,7 @@ export class Connection {
}
}
this.state = this.update(changes);
this.state = this.update({ nodes, chains, sortedNodes });
this.autoSubscribe();
}
@@ -8,34 +8,14 @@
position: relative;
}
.Chain-tabs {
position: absolute;
right: 9px;
bottom: 0;
height: 36px;
right: 5px;
bottom: 10px;
width: 200px;
text-align: right;
}
.Chain-tab-unit {
display: inline-block;
}
.Chain-tab-unit .Icon {
margin-right: 1px;
font-size: 24px;
padding: 6px;
background: #ccc;
color: #555;
cursor: pointer;
}
.Chain-tab-unit-on .Icon {
background: #222;
color: #fff;
}
.Chain-content-container {
position: absolute;
left: 0;
@@ -43,23 +43,6 @@ export namespace Chain {
}
}
function sortNodes(a: AppState.Node, b: AppState.Node): number {
if (a.pinned === b.pinned) {
if (a.blockDetails[0] === b.blockDetails[0]) {
const aPropagation = a.blockDetails[4] == null ? Infinity : a.blockDetails[4] as number;
const bPropagation = b.blockDetails[4] == null ? Infinity : b.blockDetails[4] as number;
// Ascending sort by propagation time
return aPropagation - bPropagation;
}
} else {
return Number(b.pinned) - Number(a.pinned);
}
// Descending sort by block number
return b.blockDetails[0] - a.blockDetails[0];
}
export class Chain extends React.Component<Chain.Props, Chain.State> {
constructor(props: Chain.Props) {
super(props);
@@ -157,9 +140,7 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
<Node.Row.Header settings={settings} />
<tbody>
{
nodes
.sort(sortNodes)
.map((node) => <Node.Row key={node.id} node={node} settings={settings} pins={pins} />)
nodes.map((node) => <Node.Row key={node.id} node={node} settings={settings} pins={pins} />)
}
</tbody>
</table>
@@ -220,7 +201,7 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
}
private nodes(): AppState.Node[] {
return Array.from(this.props.appState.nodes.values());
return this.props.appState.sortedNodes;
}
private pixelPosition(lat: Types.Latitude, lon: Types.Longitude): Node.Location.Position {
@@ -0,0 +1,24 @@
.Chain-Tab {
display: inline-block;
}
.Chain-Tab .Icon {
margin-right: 5px;
font-size: 24px;
padding: 6px;
color: #555;
cursor: pointer;
padding: 10px;
border-radius: 40px;
transition: background-color 0.15s linear;
}
.Chain-Tab:hover .Icon {
background: #ccc;
}
.Chain-Tab-on .Icon, .Chain-Tab-on:hover .Icon {
background: #d64ca8;
color: #fff;
}
@@ -2,6 +2,8 @@ import * as React from 'react';
import { Chain } from './';
import { Icon } from '../';
import './Tab.css';
export namespace Tab {
export interface Props {
label: string;
@@ -17,7 +19,7 @@ export class Tab extends React.Component<Tab.Props, {}> {
public render() {
const { label, icon, display, current } = this.props;
const highlight = display === current;
const className = highlight ? 'Chain-tab-unit-on Chain-tab-unit' : 'Chain-tab-unit';
const className = highlight ? 'Chain-Tab-on Chain-Tab' : 'Chain-Tab';
return (
<div className={className} onClick={this.onClick}>
+18
View File
@@ -35,8 +35,26 @@ export interface State {
subscribed: Maybe<Types.ChainLabel>;
chains: Map<Types.ChainLabel, Types.NodeCount>;
nodes: Map<Types.NodeId, State.Node>;
sortedNodes: State.Node[];
settings: Readonly<State.Settings>;
pins: Readonly<Set<Types.NodeName>>;
}
export type Update = <K extends keyof State>(changes: Pick<State, K> | null) => Readonly<State>;
export function compareNodes(a: State.Node, b: State.Node): number {
if (a.pinned === b.pinned) {
if (a.blockDetails[0] === b.blockDetails[0]) {
const aPropagation = a.blockDetails[4] == null ? Infinity : a.blockDetails[4] as number;
const bPropagation = b.blockDetails[4] == null ? Infinity : b.blockDetails[4] as number;
// Ascending sort by propagation time
return aPropagation - bPropagation;
}
} else {
return +b.pinned - +a.pinned;
}
// Descending sort by block number
return b.blockDetails[0] - a.blockDetails[0];
}