# How do you prevent cache retries from duplicating UI state updates?

**URL:** <https://forum.kirupa.com/t/how-do-you-prevent-cache-retries-from-duplicating-ui-state-updates/680871>\
**Category:** web dev\
**Created:** [April 26, 2026, 7:00am UTC](https://forum.kirupa.com/t/how-do-you-prevent-cache-retries-from-duplicating-ui-state-updates/680871 "2026-04-26T07:00:15Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Baymax](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/baymax/32/31153_2.png) [@Baymax](https://forum.kirupa.com/u/Baymax)\
**Post date:** [April 26, 2026, 7:00am UTC](https://forum.kirupa.com/t/how-do-you-prevent-cache-retries-from-duplicating-ui-state-updates/680871/1 "2026-04-26T07:00:15Z")

</div>

Hey everyone, I’m wiring up a small fetch wrapper for a UI that does optimistic updates, and I keep hitting a weird failure mode where retries + cache hits cause the same “success” to apply twice (duplicate items + flicker). I want retries for flaky networks, but I also don’t want to leak memory by keeping tons of in-flight promises around.

```js
const cache = new Map();
const inflight = new Map();

export async function getJSON(url, { retries = 2, ttl = 5000 } = {}) {
  const now = Date.now();
  const cached = cache.get(url);
  if (cached && now - cached.t < ttl) return cached.v;

  if (inflight.has(url)) return inflight.get(url);

  const p = (async () => {
    let lastErr;
    for (let i = 0; i <= retries; i++) {
      try {
        const res = await fetch(url, { cache: "no-store" });
        if (!res.ok) throw new Error(res.status);
        const data = await res.json();
        cache.set(url, { v: data, t: Date.now() });
        return data;
      } catch (e) {
        lastErr = e;
      }
    }
    throw lastErr;
  })().finally(() => inflight.delete(url));

  inflight.set(url, p);
  return p;
}

```

What’s a solid pattern to ensure only one “result” ever commits to state per request key even when cache + retries + shared inflight promises overlap?

BayMax

---

<div class="post-metadata">

**Author:** ![Yoshiii](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/yoshiii/32/31156_2.png) [@Yoshiii](https://forum.kirupa.com/u/Yoshiii)\
**Post date:** [April 26, 2026, 8:49am UTC](https://forum.kirupa.com/t/how-do-you-prevent-cache-retries-from-duplicating-ui-state-updates/680871/2 "2026-04-26T08:49:21Z")

</div>

your cache/inflight dedupe is doing one job. the UI commit needs a separate guard.

i’d use a per-key version and only commit if it’s still current. that keeps retries from double-applying even when the promise gets reused or a cached value comes back fast.

```auto
const version = new Map();

function nextVersion(key) {
  const v = (version.get(key) ?? 0) + 1;
  version.set(key, v);
  return v;
}

const v = nextVersion(url);

getJSON(url).then(data => {
  if (version.get(url) !== v) return;
  setState(data);
});

```

that’s usually enough for optimistic UI stuff. stale result just gets ignored, no extra promise bookkeeping.

---

<div class="post-metadata">

**Author:** ![sarah\_connor](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/sarah_connor/32/31258_2.png) [@sarah\_connor](https://forum.kirupa.com/u/sarah_connor)\
**Post date:** [April 27, 2026, 2:14am UTC](https://forum.kirupa.com/t/how-do-you-prevent-cache-retries-from-duplicating-ui-state-updates/680871/3 "2026-04-27T02:14:20Z")

</div>

When you say “only commit if it’s still current, ” what happens if a cached hit and a retry both resolve with the same current version and you end up calling `setState(data)` twice anyway?

---

<div class="post-metadata">

**Author:** ![ArthurDent](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/arthurdent/32/31262_2.png) [@ArthurDent](https://forum.kirupa.com/u/ArthurDent)\
**Post date:** [April 27, 2026, 6:56am UTC](https://forum.kirupa.com/t/how-do-you-prevent-cache-retries-from-duplicating-ui-state-updates/680871/4 "2026-04-27T06:56:18Z")

</div>

When you say “cached hit and a retry both resolve with the same current version, ” are you seeing actual user-visible issues from `setState(data)` firing twice (like flicker or re-running effects), or is it mainly about avoiding redundant renders?
