March 22 2025
Using Effects with Clear Boundaries
How to use effects without turning synchronization, fetches, and side effects into hard-to-predict behavior.
Andrews Ribeiro
Founder & Engineer
3 min Intermediate Frontend
Track
Senior Frontend Interview Trail
Step 6 / 15
The problem
useEffect gets messy very quickly when it becomes duct tape for every strange thing happening in the UI.
Suddenly there is an effect deriving state, another one syncing variables that never needed to exist, and another one trying to patch render order after the fact.
The code may work today, but it quickly becomes hard to predict when something runs and why it ran.
Mental model
An effect does not exist to control React’s render cycle.
An effect exists to step outside React and synchronize the component with the outside world:
- making a network request
- starting a
setInterval - attaching a global listener
- integrating a third-party library that mutates the DOM imperatively
If the logic can be calculated during render or handled inside a click handler, it probably does not belong in an effect.
A simple rule helps a lot:
- if there is no external system involved, be suspicious of the effect
Breaking it down
Before writing useEffect, ask these questions:
- Which external system am I trying to synchronize with?
- Can I calculate this value during render from what I already have?
- Should this happen only because the user clicked something?
- Is the cleanup obvious when the component unmounts?
Answering these honestly removes a surprising number of unnecessary effects.
It also helps avoid a common mistake: using an effect to “force order” when the real problem is bad state modeling or unclear responsibility.
Simple example
Look at this very common trap:
const [filteredUsers, setFilteredUsers] = useState<User[]>([])
useEffect(() => {
setFilteredUsers(users.filter((user) => user.name.includes(search)))
}, [users, search])
It looks harmless, but filteredUsers is fully derived from users and search.
That means React is being forced into an extra render step just to keep an invented piece of state in sync.
A more predictable version is:
const filteredUsers = users.filter((user) => user.name.includes(search))
The code gets easier to read and an unnecessary render disappears.
Another common case is form submission:
- worse: store
shouldSubmitin state and use an effect to trigger the request - better: do the request directly inside the submit handler
When the trigger is an explicit user action, the handler is usually clearer than an effect waiting for state to change.
Common mistakes
- using effects to derive or mirror state
- putting click or submit logic inside an effect instead of a handler
- relying on the dependency array to “fix” broken state modeling
- forgetting cleanup for timers, subscriptions, or listeners
There is another common mistake too: copying a prop into local state without a strong reason. That usually just creates one more synchronization point that can go stale.
How a senior thinks
More experienced engineers do not look at an effect and ask:
which dependency is missing?
They ask:
am I actually synchronizing with an external system, or am I using an effect to compensate for messy state modeling?
That question removes a huge number of effects before they ever land in the codebase.
Seniority shows up here in preferring less magic and more predictable flow. Good effects are usually small, explainable, and clearly connected to something outside React.
What the interviewer wants to see
In React interviews, side effects reveal your level pretty quickly.
- You understand what an effect is actually for.
- You can separate external synchronization from internal derivation.
- You care about predictability, cleanup, and avoiding chain-reaction renders.
A strong answer often sounds like this:
I use an effect when I need to synchronize the component with something external, like fetch, a timer, a listener, or an imperative library. If I can solve it during render or in the event handler, I prefer that because it keeps the flow more predictable.
A good effect connects the component to the outside world. A bad effect patches holes in your state model.
Quick summary
What to keep in your head
- Effects exist to synchronize with something outside React, not to derive state or patch rendering order.
- If the logic can happen during render or inside an event handler, it often does not need `useEffect`.
- Too much dependence on effects usually points to messy state modeling, not missing dependencies in the array.
- Clear cleanup is part of a good effect, not an optional detail.
Practice checklist
Use this when you answer
- Can I say which external system each effect is synchronizing with?
- Can I spot when an effect is only deriving state that could be calculated during render?
- Can I explain why a click-driven action usually belongs in the handler instead of an effect?
- Can I review an effect by thinking about predictability and cleanup?
You finished this article
Part of the track: Senior Frontend Interview Trail (6/15)
Share this page
Copy the link manually from the field below.