caching
This commit is contained in:
parent
473897fdfc
commit
e9cf90180f
1 changed files with 30 additions and 7 deletions
37
src/App.tsx
37
src/App.tsx
|
|
@ -1,5 +1,5 @@
|
||||||
import { z } from "zod"
|
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: Use Material UI later.
|
||||||
// TODO: Improve error-handling. Introduce some proper server response.
|
// TODO: Improve error-handling. Introduce some proper server response.
|
||||||
|
|
@ -31,13 +31,16 @@ const Dimension = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Page ===
|
// === Page ===
|
||||||
|
type PageNumber = number
|
||||||
|
|
||||||
type Page = {
|
type Page = {
|
||||||
page: number
|
page: PageNumber
|
||||||
limit: number // page size
|
limit: number // page size
|
||||||
}
|
}
|
||||||
|
type PageKey = string
|
||||||
|
|
||||||
const LIMIT = 10
|
const LIMIT = 10
|
||||||
const FIRST_PAGE = 1
|
const FIRST_PAGE: PageNumber = 1
|
||||||
|
|
||||||
const Page = {
|
const Page = {
|
||||||
init(): Page {
|
init(): Page {
|
||||||
|
|
@ -55,6 +58,10 @@ const Page = {
|
||||||
eq(page0: Page, page1: Page): boolean {
|
eq(page0: Page, page1: Page): boolean {
|
||||||
return page0.page === page1.page && page0.limit === page1.limit
|
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 ===
|
// === api ===
|
||||||
|
|
@ -145,13 +152,31 @@ type Msg =
|
||||||
function useApp(): [State, Dispatch] {
|
function useApp(): [State, Dispatch] {
|
||||||
const [state, dispatch] = useReducer(update, State.init())
|
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 ===
|
// === initialization & reloading ===
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// TODO: error-handling
|
// TODO: error-handling
|
||||||
getImageIds(state.page).then((imageIds) => {
|
getImageIdsCached(state.page).then((imageIds) => {
|
||||||
dispatch({ tag: "imagesReceived", forPage: state.page, 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 {
|
function update(state: State, msg: Msg): State {
|
||||||
switch (msg.tag) {
|
switch (msg.tag) {
|
||||||
|
|
@ -162,7 +187,6 @@ function useApp(): [State, Dispatch] {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
case "previousButtonClicked": {
|
case "previousButtonClicked": {
|
||||||
console.log("prev")
|
|
||||||
const newPage = Page.previous(state.page)
|
const newPage = Page.previous(state.page)
|
||||||
if (Page.eq(newPage, state.page)) {
|
if (Page.eq(newPage, state.page)) {
|
||||||
return state // preserves identity
|
return state // preserves identity
|
||||||
|
|
@ -171,7 +195,6 @@ function useApp(): [State, Dispatch] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "nextButtonClicked": {
|
case "nextButtonClicked": {
|
||||||
console.log("next")
|
|
||||||
return { ...state, page: Page.next(state.page), imageIds: Remote.loading() }
|
return { ...state, page: Page.next(state.page), imageIds: Remote.loading() }
|
||||||
}
|
}
|
||||||
case "imageClicked": {
|
case "imageClicked": {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue