Skip to content
why does my useEffect fetch run twice?? deps array is empty
question1 min read125 viewsSep 5, 2026

why does my useEffect fetch run twice?? deps array is empty

Kevin Tran@kevtran_dev

September 5, 2026 · 1 min read

#3 in Rerender Anonymous

125 views3 comments

ok this is probably dumb but i have been staring at it for an hour. i have a component that fetches the users orders when it mounts. deps array is empty so it should only run once right?? but in the network tab i see TWO requests every time

useEffect(() => {
  fetch('/api/orders')
    .then((res) => res.json())
    .then((data) => setOrders(data))
}, [])

i console logged inside it and yep, runs twice. react 18 with vite. is something re-mounting my component? i checked and the parent isnt changing keys or anything. my senior is out today and i dont want to wait till tomorrow lol

Accepted answer

Grace Wanjiru#1 in Rerender Anonymous2 likes

Not dumb at all, everyone hits this one. In development, React 18 StrictMode mounts your component, unmounts it, and mounts it again on purpose, so effects run twice. It is checking that your effect cleans up after itself. In production it runs once. The fix is not to remove StrictMode, it is to add a cleanup so the first request gets cancelled: useEffect(() => { const controller = new AbortController() fetch('/api/orders', { signal: controller.signal }) .then((res) => res.json()) .then(setOrders) .catch((err) => { if (err.name !== 'AbortError') throw err }) return () => controller.abort() }, []) When you have more of these, look at TanStack Query, it handles all of this for you.

View in the thread

Written by

Kevin Tran

used to run a pho kitchen, now junior frontend dev in houston (8 months in!!). learning react, typescript and how to not panic in standups. ask me about broth

Get the best of #rerender-anonymous weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

3

Sign in to join the conversation.

Sign in to comment

Accepted answer

Grace Wanjiru@grace_wanjiru#1 in Rerender Anonymous
Not dumb at all, everyone hits this one. In development, React 18 StrictMode mounts your component, unmounts it, and mounts it again on purpose, so effects run twice. It is checking that your effect cleans up after itself. In production it runs once. The fix is not to remove StrictMode, it is to add a cleanup so the first request gets cancelled: useEffect(() => { const controller = new AbortController() fetch('/api/orders', { signal: controller.signal }) .then((res) => res.json()) .then(setOrders) .catch((err) => { if (err.name !== 'AbortError') throw err }) return () => controller.abort() }, []) When you have more of these, look at TanStack Query, it handles all of this for you.
2 net votes
Kevin Tran@kevtran_dev#3 in Rerender Anonymous
OMG its StrictMode. i saw it in main.tsx and had no idea what it did. added the abort controller and now the first request shows as cancelled in the network tab. thank you grace!!
0 net votes
Tomasz Kowalski@tomasz_k#5 in Rerender Anonymous
In my day the framework did not run your code twice to teach you lesson. But ok, this lesson is good one.
0 net votes