Back to Home
Why My Analytics Was Logging Every Page Visit Twice (And How I Fixed It)

Why My Analytics Was Logging Every Page Visit Twice (And How I Fixed It)

B
Blizine Admin
·2 min read·0 views

Vicente G. Reyes Posted on Jun 1 • Originally published at vicentereyes.org Why My Analytics Was Logging Every Page Visit Twice (And How I Fixed It) # python # django # react # typescript I built a custom analytics system into my portfolio backend — a Django REST API that records page visits — and wired the React frontend to call it whenever someone lands on a project or blog post detail page. It worked, except for one problem: every visit was logged twice. This is the story of chasing the wrong root cause, wasting time on a fix that didn't work, and landing on a dead-simple solution that lives outside React entirely. The Setup The backend is a Django REST API with a PageVisit model. The frontend calls a single endpoint whenever a user lands on a detail page: // POST /api/analytics/track/ { " page_type " : " project " , " object_id " : 33 } Enter fullscreen mode Exit fullscreen mode On the React side, the call lives in a useEffect inside ProjectPage : useEffect (() => { if ( project ?. id ) api . trackPageView ( ' project ' , Number ( project . id )); }, [ project ?. id ]); Enter fullscreen mode Exit fullscreen mode The project data comes from a useProject hook that reads from localStorage cache first, then fetches from the API. The Symptom After visiting two project pages, the admin showed this: 637 Project 33 636 Project 33 635 Personal Projects - 634 Project 30 633 Project 30 Enter fullscreen mode Exit fullscreen mode Two records per project visit, every single time. First Suspect: React StrictMode My first instinct was React StrictMode. In development, <React.StrictMode> intentionally double-invokes effects to surface side effects — it mounts the component, runs cleanup, then remounts it. If the effect fired on both the original mount and the simulated remount, that would explain exactly two records per visit. The standard fix is a useRef guard: store the last tracked ID in a ref, and skip the call if it matches. const trackedProjectId = useRef <

📰Dev.to — dev.to

Comments