Posted on Jul 15 • Edited on Jul 16
When I started learning React, I thought useEffect was the solution to almost everything. To derive state, to fetch data, to synchronize props or calculate a value, the answer for all this was useEffect.
After working on larger applications, i realized most of those effects weren’t needed at all.
Today, I probably write 80% fewer useEffects than I did a few years ago. Not because it’s a bad hook but because most problems have simpler solutions.
This article is the first part of my Modern React Patterns series, where I go over patterns that worked fine in small demos but became painful in production.
What useEffect Is Actually For
The React documentation describes effects as a way to synchronize your component with external systems.
That means things like:
- network requests
- WebSockets
- timers
- browser APIs
- subscriptions
- third-party libraries
If your effect isn’t interacting with something outside React, there’s a good chance you don’t need one.
1. Don’t Use useEffect For
One of the most common examples I still see is this:
const [fullName, setFullName]=useState("");
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
Enter fullscreen mode Exit fullscreen mode
It works but React now renders twice. The process that now is doing is the following:
render –> effect runs –> state changes –> render again
and there’s no reason for that.
Instead i do this:
const fullName=`${firstName} ${lastName}`;
Enter fullscreen mode Exit fullscreen mode
2. Don’t Synchronize Props Into State
const [user, setUser] = useState(props.user);
useEffect(() => {
setuser(props.user);
}, [props.user]);
Enter fullscreen mode Exit fullscreen mode
Most of the times this creates two sources of truth. Eventually one of them gets updated and the other doesn’t. Unless you intentionally want a local editable copy, just use the prop directly. It’s much simpler
function prodile({user}){
return <h2>{user.name}</h2>;
}
Enter fullscreen mode Exit fullscreen mode
3. Don’t Calculate Values Inside an Effect
I’ve seen code like this many times.
const[filteredUsers, setFilteredUsers]=useStat([]);
useEffect(() => {
setFilteredUsers(
users.filter(user=> user.active)
);
}, [users]);
Enter fullscreen mode Exit fullscreen mode
That’s unnecessary state, i’ts way better:
const filteredUsers=users.filter(
user=> user.active
);
Enter fullscreen mode Exit fullscreen mode
If the computation is actually expensive:
const filteredUsers=useMemo(() => {
return users.filter(user=> user.active):
}, [users]);
Enter fullscreen mode Exit fullscreen mode
Notice that useMemo is an optimization, not a replacement for every calculation.
4. Debugging Is One of The Few Places Where I Still Use Effects A Lot
When debugging state updates I often do this:
useEffect(() => {
console.log(user);
}, [user]);
Enter fullscreen mode Exit fullscreen mode
It’s incredibly useful while debugging but before merging the code, delete it.
5. Fetching Data With useEffect Isn’t Always The Best Choice Anymore
Years ago almost every React project looked like this:
useEffect(() => {
fetch("/api/users")
.then(res=> res.json())
.then(setUsers);
}, []);
Enter fullscreen mode Exit fullscreen mode
Nowadays we have much better alternatives. Libraries like TanStack Query or SWR solve things that you eventually end up building yourself:
- caching
- retries
- background refetching
- loading state
- error state
- deduplication And instead of all of this, you can:
const {data, isLoading}= useQuery({
queryKey: ["users"]
queryFn: getUsers
});
Enter fullscreen mode Exit fullscreen mode
That’s less code and you get fewer bugs and better developer experience.
If you’re interested in API reliability, I wrote another article about common integration mistakes:
“Building Reliable API integrations in Modern Web Applications”
6. Effects Often Hide Architecture Problems
Something I’ve noticed over time is whenever I find myself writing lots of effects indife the same component and it’s usually because the component is trying to do too much. For example:
- fetching
- filtering
- sorting
- formatting
- validation
- event handling
- rendering
and everything together. Splitting responsabilities into smaller hooks or components ofter removes half of those effects automatically.
When You SHOULD Use useEffect
None of this means “never use useEffect”. There are plenty of legitimate use cases to use it, and let me tell some of them.
WebSocket connections:
useEffect(() => {
const socket = new WebSocket(url);
return() => socket.close();
}, []);
Enter fullscreen mode Exit fullscreen mode
Timers:
useEffect(() => {
const id= setInterval(fetchData, 5000);
return() => clearInterval(id);
}, []);
Enter fullscreen mode Exit fullscreen mode
Browser APIs:
useEffect(() => {
window.addEventListener("resize", onResize);
return() => {
window.removeEventListener("resize", onResize);
};
}, []);
Enter fullscreen mode Exit fullscreen mode
Third-party libraries:
useEffect(() => {
const chart= new Chart(canvas);
return()=> chart.destroy();
}, []);
Enter fullscreen mode Exit fullscreen mode
These are exactly the kinds of external systems effects were designed for.
My Personal Rule
Whenever I’m about to write an effect, I stop for a second and ask myself if am I synchronizing with an external system or am I compensating for my component design. That question alone has removed a surprising amount of unnecessary code from my projects.
Conclusion
useEffect isn’t bad. It’s just much easier to overuse than most React hooks. Today I try to use:
- derived values instead of derived state
- props instead of synchronized state
- TanStack Query for server state
- custom hooks for reusable logic
- effects only when I actually need to synchronize with something outside React
And you can get fewer renders, less state, fewer bugs and components that are easier to understand months later.
Next Articles in This Series
Part 2: Stop Fetching Data Inside useEffect
Part 3: Stop Syncing Props Into State
Part 4: Event handlers are usually the better place for side effect
Part 5: Stop Using Effects for State Resets
Thanks for reading!! If you’re looking for help with an existing project or need someone to solve a tricky frontend/backend issue, you can also find my freelance profiles through my Dev.to profile.
답글 남기기