From 1786e1cac9a02d1357dedcbef5bbbabfb17e3f8c Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Tue, 24 Dec 2024 14:54:23 +0100 Subject: [PATCH] Improve loading hook, reduce query API spam --- packages/frontend/src/hooks/useLoading.ts | 8 +- packages/frontend/src/routes/Home.tsx | 125 ++++++++++++---------- packages/frontend/src/routes/Piece.tsx | 2 +- 3 files changed, 76 insertions(+), 59 deletions(-) diff --git a/packages/frontend/src/hooks/useLoading.ts b/packages/frontend/src/hooks/useLoading.ts index 73244da..7382808 100644 --- a/packages/frontend/src/hooks/useLoading.ts +++ b/packages/frontend/src/hooks/useLoading.ts @@ -1,7 +1,7 @@ import { mapProp, Updater, useStore } from "@/hooks/useStore"; import { Treaty } from "@elysiajs/eden"; import { Effect, Fiber, pipe } from "effect"; -import { useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; export type LoadingResult> = @@ -93,11 +93,13 @@ function mapFailure(error: E) { return Object.freeze({ isLoading: false as const, data: null, error }); } -export function useLoadingEffect(effect: Effect.Effect) { +export function useLoadingEffect(effect: Effect.Effect, deps: React.DependencyList) { const [result, setResult] = useState>(IS_LOADING); useEffect(() => { + setResult(IS_LOADING); + const fiber = pipe( effect, Effect.match({ @@ -117,7 +119,7 @@ export function useLoadingEffect(effect: Effect.Effect) { return () => { Effect.runFork(interruptEffect); }; - }, []); + }, deps); return result; } diff --git a/packages/frontend/src/routes/Home.tsx b/packages/frontend/src/routes/Home.tsx index f18569f..35e3e62 100644 --- a/packages/frontend/src/routes/Home.tsx +++ b/packages/frontend/src/routes/Home.tsx @@ -5,9 +5,9 @@ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogT import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { useLoading, useLoadingEffect } from "@/hooks/useLoading"; +import { useLoadingEffect } from "@/hooks/useLoading"; import { PieceId } from "common"; -import { Cause, Effect, Option } from "effect"; +import { Cause, Clock, Duration, Effect, Option } from "effect"; import { Loader2, Plus } from "lucide-react"; import { FormEventHandler, ReactNode, useId, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; @@ -17,64 +17,79 @@ export function Home() { const [name, setName] = useState(""); const [author, setAuthor] = useState(""); - const { isLoading, error, data: pieceIds } = useLoading(() => client.piece.get({ - query: { - ...(name !== "" ? { name } : undefined), - ...(author !== "" ? { author } : undefined), - }, - }), [name, author]); + const { isLoading, error, data: pieceIds } = useLoadingEffect(Effect.gen(function* () { + yield* Clock.sleep(Duration.millis(500)); + const { error, data } = yield* Effect.promise((signal) => client.piece.get({ + query: { + ...(name !== "" ? { name } : undefined), + ...(author !== "" ? { author } : undefined), + }, + fetch: { signal }, + })); - if (isLoading) { - return ( -
-
Ładowanie…
-
- ); - } + if (error !== null) { + return yield* Effect.fail(error); + } else { + return data; + } + }), [name, author]); return (
- {error !== null ? ( - Cause.isUnknownException(error) ? "Wystąpił nieznany błąd" : `Wystąpił błąd: ${error.value}` - ) : (<> -
- - - - - - - setName(e.target.value)} - /> - setAuthor(e.target.value)} - /> -
- - +
+ + + + + + + setName(e.target.value)} + /> + setAuthor(e.target.value)} + /> +
+
+ + + Tytuł + Twórcy + Dodano + Zmodyfikowano + + + + {isLoading ? ( - Tytuł - Twórcy - Dodano - Zmodyfikowano + +
+ + Ładowanie… +
+
- - - {pieceIds.map((pieceId) => )} - -
- )} + ) : error !== null ? ( + + + {Cause.isUnknownException(error) ? "Wystąpił nieznany błąd" : `Wystąpił błąd: ${error.value}`} + + + ) : ( + pieceIds.map((pieceId) => ) + )} + +
); } @@ -87,7 +102,7 @@ namespace PieceRow { function PieceRow(props: PieceRow.Props) { - const { isLoading, error, data: piece } = useLoadingEffect(Effect.uninterruptible(pieceCache.get(props.pieceId))); + const { isLoading, error, data: piece } = useLoadingEffect(Effect.uninterruptible(pieceCache.get(props.pieceId)), [props.pieceId]); if (isLoading) { return ( diff --git a/packages/frontend/src/routes/Piece.tsx b/packages/frontend/src/routes/Piece.tsx index 54c966b..ad7bf59 100644 --- a/packages/frontend/src/routes/Piece.tsx +++ b/packages/frontend/src/routes/Piece.tsx @@ -20,7 +20,7 @@ export function Piece() { const id = PieceId(useParams().pieceId!); - const { isLoading, error, data, setData } = useLoadingEffect(Effect.uninterruptible(pieceCache.get(id))); + const { isLoading, error, data, setData } = useLoadingEffect(Effect.uninterruptible(pieceCache.get(id)), [id]); const setAttachments = useCallback((action: Update) => { setData!(mapProp("attachments", action));