🐛 Bug: React useEffect causes duplicate API requests in development #212711
🏷️ Discussion TypeBug 💬 Feature/Topic AreaCode quality Discussion DetailsDescriptionI've noticed that an API request inside Consider this example: import { useEffect, useState } from "react";
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("/api/users")
.then((res) => res.json())
.then(setUsers);
}, []);
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default Users;Application entry point: import React from "react";
import ReactDOM from "react-dom/client";
import Users from "./Users";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<Users />
</React.StrictMode>
);Expected BehaviorThe component should fetch the users without causing unnecessary duplicate network requests. Actual BehaviorIn development mode, React StrictMode performs an additional setup and cleanup cycle for Effects. As a result, the effect above can trigger two GET requests. This behavior does not normally occur in the same way in production builds. Possible SolutionOne approach is to use useEffect(() => {
const controller = new AbortController();
fetch("/api/users", {
signal: controller.signal,
})
.then((res) => res.json())
.then(setUsers)
.catch((error) => {
if (error.name !== "AbortError") {
console.error(error);
}
});
return () => controller.abort();
}, []);Questions
Environment:
|
Replies: 3 comments
|
💬 Your Product Feedback Has Been Submitted 🎉 Thank you for taking the time to share your insights with us! Your feedback is invaluable as we build a better GitHub experience for all our users. Here's what you can expect moving forward ⏩
Where to look to see what's shipping 👀
What you can do in the meantime 💻
As a member of the GitHub community, your participation is essential. While we can't promise that every suggestion will be implemented, we want to emphasize that your feedback is instrumental in guiding our decisions and priorities. Thank you once again for your contribution to making GitHub even better! We're grateful for your ongoing support and collaboration in shaping the future of our platform. ⭐ |
|
I think using However, I wouldn't consider it a complete solution for every duplicate request scenario. React StrictMode intentionally runs an additional Effect setup and cleanup cycle in development to help identify missing cleanup logic. So disabling StrictMode just to avoid duplicate requests isn't really the right fix. For larger applications, I'd prefer using TanStack Query because it provides request deduplication, caching, automatic retries, and better control over server state. My recommendation:
One important detail: aborting a request doesn't guarantee that the server never received or processed it. This is especially important for POST, PUT, and other non-idempotent operations. |
|
. |
I think using
AbortControlleris a good approach for handling this particular case, especially when working with nativefetch().However, I wouldn't consider it a complete solution for every duplicate request scenario.
React StrictMode intentionally runs an additional Effect setup and cleanup cycle in development to help identify missing cleanup logic. So disabling StrictMode just to avoid duplicate requests isn't really the right fix.
For larger applications, I'd prefer using TanStack Query because it provides request deduplication, caching, automatic retries, and better control over server state.
My recommendation:
AbortControllerfor simple component-level requests.