Master useState and useEffect with a clear mental model. Stop guessing how React renders and start writing predictable, bug-free components today.

I remember sitting at my desk three years ago, staring at a flickering component that wouldn't stop re-rendering. I had just discovered useEffect, and I was convinced it was the "onMount" lifecycle method I knew from older frameworks. I was wrong, and my app paid for it with roughly 1.8x more network requests than it actually needed.
If you’re struggling with these hooks, it’s usually because you’re trying to force an imperative mindset onto a declarative library. Let’s fix that.
When you call useState, you aren't just creating a variable. You’re asking React to keep track of a value across renders. Most beginners think const [count, setCount] = useState(0) is like a global variable that updates instantly. It isn't.
Think of each render as a frozen photograph of your component. When a render happens, the count variable is locked to the value it had when that specific function execution started.
JAVASCRIPTconst handleClick = () => { setCount(count + 1); setCount(count + 1); setCount(count + 1); };
If count starts at 0, you might expect the final value to be 3. It will actually be 1. Why? Because inside this specific render, count is 0. You’re calling setCount(0 + 1) three times. React schedules these updates, but they all resolve to the same result. When you need the previous value, use the functional update pattern: setCount(prev => prev + 1). It’s the difference between a bug and a feature.

The biggest mistake I see juniors make is treating useEffect as a "lifecycle event." We often try to map it to componentDidMount. Please, stop doing that.
useEffect is a synchronization mechanism. You are telling React: "Synchronize this piece of the DOM or this side effect with these specific values."
If your dependency array is [data], you aren't saying "run this when the component mounts." You’re saying "every time data changes, re-run this logic to keep the UI in sync." If you leave the array empty, you’re saying "this only needs to sync once."
I once tried to fetch user data inside a component and update state based on that data, all while listing the state variable in the dependency array. It caused an infinite loop that crashed my browser tab in about 280ms.
useEffect runs because the dependency changed.setUserData triggers a state update.If you find yourself in an infinite loop, check if your effect is updating the very state that triggers it. If you’re building a complex application, you might eventually move this data fetching into a dedicated layer, much like how we handle complex data structures when mastering headless WordPress with Next.js ISR. Keep your effects clean and focused on synchronization, not logic orchestration.
The dependency array is your contract with React. If you include a variable in the effect, you must include it in the array. If you don't, you’ll encounter "stale closures."
A stale closure happens when your effect is still looking at the variable from the first render, even though the state has updated ten times since. It’s like looking at a map from 1995 while driving through a modern city. You’ll be lost, and your users will be confused.

useEffect to derive state from props.useEffect is longer than 15 lines, you’re probably doing too much. Break it into smaller, specific effects.I still find myself debugging race conditions in complex effects sometimes. It’s a part of the job. The goal isn't to write perfect code on the first try; it’s to build a mental model that lets you identify why your component is behaving strangely.
Next time you're stuck, ask yourself: "Am I trying to perform an action, or am I trying to keep the UI in sync with a value?" If you answer that honestly, the useEffect dependency array usually fills itself out. What are you still struggling with regarding these hooks? I’m still learning how to manage complex state transitions without reaching for external libraries, and there's always a cleaner way to write it.
React props and state management can be confusing. Learn the right way to structure your data, avoid prop drilling, and keep your components predictable.