This commit is contained in:
Yura Dupyn 2026-05-14 23:48:28 +02:00
parent 473897fdfc
commit e9cf90180f

View file

@ -1,5 +1,5 @@
import { z } from "zod"
import { useReducer, useEffect, createContext, useContext } from "react"
import { useReducer, useRef, useEffect, createContext, useContext } from "react"
// TODO: Use Material UI later.
// TODO: Improve error-handling. Introduce some proper server response.
@ -31,13 +31,16 @@ const Dimension = {
}
// === Page ===
type PageNumber = number
type Page = {
page: number
page: PageNumber
limit: number // page size
}
type PageKey = string
const LIMIT = 10
const FIRST_PAGE = 1
const FIRST_PAGE: PageNumber = 1
const Page = {
init(): Page {
@ -55,6 +58,10 @@ const Page = {
eq(page0: Page, page1: Page): boolean {
return page0.page === page1.page && page0.limit === page1.limit
},
// for hashing in maps to avoid identity problems
key(page: Page): PageKey {
return `${page.page}:${page.limit}`
},
}
// === api ===
@ -145,13 +152,31 @@ type Msg =
function useApp(): [State, Dispatch] {
const [state, dispatch] = useReducer(update, State.init())
// === Caching API calls ===
// Could also cache `Promise<ImageId[]>` in case two requests are made really fast one after another (not really the case in this app so whatever)
const cacheRef = useRef<Map<PageKey, ImageId[]>>(new Map())
async function getImageIdsCached(page: Page): Promise<ImageId[]> {
const pageKey = Page.key(page)
const maybeImages = cacheRef.current.get(pageKey)
if (maybeImages === undefined) {
console.log("CACHE-MISS")
const images = await getImageIds(page)
cacheRef.current.set(pageKey, images)
return images
} else {
console.log("CACHE-HIT")
return maybeImages
}
}
// === initialization & reloading ===
useEffect(() => {
// TODO: error-handling
getImageIds(state.page).then((imageIds) => {
getImageIdsCached(state.page).then((imageIds) => {
dispatch({ tag: "imagesReceived", forPage: state.page, imageIds })
})
}, [state.page])
}, [state.page]) // Would have been amazing if we could put `Page.key(state.page)` inside of this. Then the trouble with identity would be gone. But we can't, because React compiler and linter would complain /facepalm
function update(state: State, msg: Msg): State {
switch (msg.tag) {
@ -162,7 +187,6 @@ function useApp(): [State, Dispatch] {
return state
}
case "previousButtonClicked": {
console.log("prev")
const newPage = Page.previous(state.page)
if (Page.eq(newPage, state.page)) {
return state // preserves identity
@ -171,7 +195,6 @@ function useApp(): [State, Dispatch] {
}
}
case "nextButtonClicked": {
console.log("next")
return { ...state, page: Page.next(state.page), imageIds: Remote.loading() }
}
case "imageClicked": {