← Back to list

A cache-invalidation strategy: a new strategy break down

Alright, let’s break it down the new strategy.

Franklyn Edekobi · 2025-10-24 21:26 · 1 claps · 2.6 min read
#react #typescript #cache #web-browser #etag
Open on Medium ↗
Wiki topics: 🌐 · Web Development

A cache-invalidation strategy: a new strategy break down

A mad in his mid-sixties saying ‘Alright let’s break it down’

A mad in his mid-sixties saying ‘Alright let’s break it down’

Alright, let’s break it down the new strategy.

First part is just setup, all the options you can pass in. You can control how often it checks, whether it saves version info in local or session storage, and what should happen when a new version is found.

versionFile = '/version.json'
checkInterval = 6
throttleMs = 3
versionCheckType = 'polling'
timeUnit = 'minutes'
storage = 'local'

Basically, it’s like saying:

“Check this file every 6 minutes, but don’t spam it too often, and remember what version I’ve seen before.”

It’s flexible, you can make it check on route changes, or only when the tab is active.

Next, I set up some refs to store the last known values.

const lastEtagRef = useRef<string | null>(null);
const lastVersionRef = useRef<string | null>(null);
const prevLocation = useRef<string | null>(null);

These just keep small bits of memory inside the hook, the current version, ETag, and the last route the user was on. I also added a lastCheckTimeRef so the hook can throttle itself and not check too often.

Then there’s this helper to decide where to save stuff:

const getStorage = useCallback((): Storage | null => {
  if (storage === 'local') return window.localStorage;
  if (storage === 'session') return window.sessionStorage;
  return null;
}, [storage]);

Nothing fancy, just helps the hook remember which version the user saw last time, even after a refresh. So if the app reloads, it doesn’t forget everything.

Now the main brain of it all, the checkVersion function.

This is where the magic happens. Every time it runs, it tries to fetch /version.json, but with a small twist, it uses the ETag header.

const headers: Record<string, string> = {};
if (lastEtagRef.current) {
  headers['If-None-Match'] = lastEtagRef.current;
}

That If-None-Match tells the server,

“Hey, I already have this version. Has anything changed?”

If nothing changed, the server just replies 304 Not Modified, meaning “You’re good.” So no need to download the file again. But if there’s a new build, the server sends the new file, and we detect the change.

That’s what this part does:

if (oldBuildTime && newBuildTime !== oldBuildTime) {
  onNewVersion?.(versionCheckAction);
}

Once a new version is detected, we call onNewVersion, and the app can show a toast or reload.

Simple and neat.

Then I store the new values again:

lastVersionRef.current = newBuildTime;
lastEtagRef.current = newEtag;

So that next time, the app knows exactly what version it last saw. And I save it in storage too, in case the user refreshes or opens a new tab.

Next part is where it decides when to check for updates.

If you picked “polling,” it’ll check every few minutes, but only when the page is visible.

if (document.visibilityState === 'visible') {
  checkVersion('prompt-user').catch(console.error);
}

That “visible” check is a small thing, but it matters. It means if someone has your app open in a background tab, it won’t keep wasting network calls. Once they come back, it checks instantly.

Then there’s this focus and visibility event handler:

window.addEventListener('focus', handler);
document.addEventListener('visibilitychange', handler);

So anytime the user switches back to the app tab, boom, it checks for new updates. No waiting. No delay. It just works quietly in the background.

Finally, for apps that navigate a lot inside (like React SPAs), I added another trigger.

if (versionCheckType === 'route-change')

This one checks whenever the user moves to a new route or page inside the app. It’s nice because people naturally move around, so the check feels almost invisible.

And that’s it.

An animated gif of a man in suit crying tears of joy and saying thank you

An animated gif of a man in suit crying tears of joy and saying thank you


메타데이터
post_id
bb3f81a5f092
slug
a-cache-invalidation-strategy-a-new-strategy-break-down-bb3f81a5f092
url
https://medium.com/@edekobifrank/a-cache-invalidation-strategy-a-new-strategy-break-down-bb3f81a5f092
canonical_url
https://medium.com/@edekobifrank/a-cache-invalidation-strategy-a-new-strategy-break-down-bb3f81a5f092
author_url
https://medium.com/@edekobifrank
status
ok
fetched_at
2026-07-16 05:02:48