From 41f9d5e2e0da6240b8bd0b53b0876d7139c99f74 Mon Sep 17 00:00:00 2001 From: Denys Kashkovskyi Date: Sat, 16 Apr 2022 19:04:41 +0000 Subject: [PATCH] Optimize client rendering --- web/client/src/main.tsx | 87 +++++++++++++++++++++------------------- web/client/src/stream.ts | 46 +++++++++++++++++++-- 2 files changed, 88 insertions(+), 45 deletions(-) diff --git a/web/client/src/main.tsx b/web/client/src/main.tsx index 09fd0e4..2207164 100644 --- a/web/client/src/main.tsx +++ b/web/client/src/main.tsx @@ -1,4 +1,4 @@ -import { lift } from '@grammarly/focal' +import { F, lift, ReadOnlyAtom } from '@grammarly/focal' import { Chip, Paper, @@ -10,7 +10,7 @@ import { TableRow } from '@mui/material' import * as React from 'react' -import { map } from 'rxjs' +import { map, Observable } from 'rxjs' import { Stream } from './stream' const Body = lift(TableBody) @@ -19,43 +19,48 @@ const Tcp = ({ item }: { item: Stream.Item }) => ( ) -export const Main = () => ( -
- - - - - Address - TCP status - HTTP status - Request duration - - - - {Stream.create().pipe( - map(items => - items.map(item => ( - - - {item.id} - - - - - {item.httpResponse} - {item.duration} - - )) - ) - )} - -
-
-
+const Row = ({ item }: { item: Observable }) => ( + + {item.pipe( + map(item => ( + + + {item.id} + + + + + {item.httpResponse} + {item.duration} + + )) + )} + ) + +export const Main = () => { + const { ids, getItem } = Stream.create() + return ( +
+ + + + + Address + TCP status + HTTP status + Request duration + + + {ids.pipe(map(ids => ids.map(id => )))} +
+
+
+ ) +} diff --git a/web/client/src/stream.ts b/web/client/src/stream.ts index a5f272f..211703d 100644 --- a/web/client/src/stream.ts +++ b/web/client/src/stream.ts @@ -1,10 +1,23 @@ import { pipe } from 'fp-ts/es6/function' import { isNumber } from 'fp-ts/es6/number' import * as Ord from 'fp-ts/es6/Ord' -import { fromEvent, map, mergeMap, Observable, retryWhen, scan, take, timer } from 'rxjs' +import { + distinctUntilChanged, + fromEvent, + map, + mergeMap, + Observable, + retryWhen, + scan, + share, + take, + timer +} from 'rxjs' import { WebSocketSubject } from 'rxjs/webSocket' +import { boolean, string } from 'fp-ts' +import { Atom } from '@grammarly/focal' +import * as Eq from 'fp-ts/es6/Eq' import * as A from 'fp-ts/es6/Array' -import { string } from 'fp-ts' export type Stream = Observable @@ -26,6 +39,15 @@ export namespace Stream { StatusErrResponse = 'ErrorResponse' } + export const eq: Eq.Eq = Eq.struct({ + id: string.Eq, + inProgress: boolean.Eq, + tcp: string.Eq, + httpResponse: string.Eq, + duration: string.Eq, + status: string.Eq + }) + export const ord: Ord.Ord = pipe( Ord.contramap(x => x.status)(string.Ord), Ord.reverse @@ -65,7 +87,7 @@ export namespace Stream { export const create = () => { const sock = new WebSocketSubject(`ws://${location.host}/ws`) - return sock.pipe( + const collection = sock.pipe( retryWhen(errors => errors.pipe( mergeMap(() => { @@ -82,7 +104,23 @@ export namespace Stream { (a, c) => (a.set(c.id, c), a), new Map() ), - map(items => pipe([...items.values()], A.sort(Stream.Item.ord))) + share() + ) + + const getItem = (id: string) => + collection.pipe( + map(c => c.get(id)!), + distinctUntilChanged(Stream.Item.eq.equals) + ) + + const ids = collection.pipe( + map(c => [...c.keys()]), + distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)) ) + + return { + ids, + getItem + } } }