Development mode mounting, unmounting and remounting every component is a diagnostic, and the double-fetch it produces is the diagnosis rather than the bug.
useEffect(() => {
const controller = new AbortController()
fetch(`/api/orders/${id}`, { signal: controller.signal })
.then(r => r.json())
.then(setOrder)
.catch(e => { if (e.name !== 'AbortError') throw e })
return () => controller.abort()
}, [id])
An effect that cannot survive being run twice cannot survive a fast navigation either, so the double invocation is finding a real race rather than creating one. The abort in the cleanup is the general answer and the specific one that matters: without it, two responses arrive and the slower one wins, which is exactly the stale-render bug that is otherwise impossible to reproduce.