dev-resources.site
for different kinds of informations.
Why React Can Surprise You (And How to Tame It)
If youâve ever worked with React, youâve probably had moments of self-doubt. Iâve been there tooâwondering if Iâm missing something fundamental, only to discover that the issue wasnât with me but with Reactâs peculiarities.
Here, Iâll share some of these unexpected behaviors, the reasons behind them, and my own experiences. Hopefully, this will save you some of the head-scratching Iâve endured!
1. State Updates: The Delayed Reaction
You call setState
, but the UI doesnât change immediately. Why? React batches state updates for performance. This often catches new developers off guard because it contradicts the expectation of instant feedback.
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); // Still logs the old count
};
Why It Happens:
Reactâs state updates are asynchronous. This allows React to optimize re-renders, but it means that setState
doesnât immediately update count
. The new state will only be reflected after the component re-renders.
Pro Tip: Use the functional form of setState
to avoid relying on stale state:
setCount(prevCount => prevCount + 1);
2. Dependency Arrays in Hooks: Why Didnât This Re-Run?
Another common pitfall: youâve added a useEffect
hook, but it doesnât seem to run when you expect it to.
useEffect(() => {
console.log("Effect ran");
}, [someVar]);
Then, you realize that updating someVar
doesnât trigger the effect. After debugging, you discover that someVar
is an object or array.
Why It Happens:
Reactâs dependency array uses reference equality. Even if two objects or arrays look identical, theyâre considered different if their references differ. This can lead to unexpected behavior if youâre not careful.
Pro Tip: Use a utility like useDeepCompareEffect
or memoize your dependencies.
3. Re-Rendering Woes: Why Did This Re-Render?
You optimize your component, only to find itâs still re-rendering unnecessarily.
const MyComponent = ({ count }) => {
console.log("Rendered");
return <div>{count}</div>;
};
Even if count
doesnât change, the component re-renders because the parent component re-renders and passes a new prop reference.
Why It Happens:
Reactâs default behavior is to re-render child components unless you use optimizations like React.memo
.
Pro Tip: Use React.memo
to memoize your components or useCallback
to prevent prop references from changing unnecessarily.
4. The Key Prop: Whatâs the Big Deal?
You forget to add a key
to your list items, and suddenly your UI behaves erratically.
<ul>
{items.map(item => (
<li>{item}</li>
))}
</ul>
Why It Happens:
React uses key
to track which items have changed, been added, or removed. Without unique keys, React may re-use DOM nodes incorrectly.
Pro Tip: Use unique identifiers as keys (e.g., id
from your data).
5. Event Handling: The Mysterious Double Trigger
Ever had a button click event fire twice and wondered if your browser was haunted?
<button onClick={() => console.log("Clicked!")}>Click Me</button>
You click the button, and âClicked!â shows up in the console twice in development mode.
Why It Happens:
Reactâs Strict Mode intentionally mounts and unmounts components twice during development to highlight potential side effects. This can cause event handlers to fire multiple times.
Pro Tip: Don't panicâitâs only in development.
6. Uncontrolled Components: Whoâs in Control Here?
You create an input element and assign it a value, expecting React to manage it seamlessly, but you get a warning.
<input value="hello" />
React throws a warning about a âcontrolled component.â
Why It Happens:
React distinguishes between controlled components (managed by React state) and uncontrolled components (managed by the DOM). Mixing the two causes issues.
Pro Tip: Always pair value with onChange if you want a controlled component:
<input value={val} onChange={(e) => setVal(e.target.value)} />
7. Refs: Why Canât I Just Use a Regular Variable?
You try to store a value between renders using a variable, but it resets every time.
let count = 0;
const increment = () => {
count += 1;
console.log(count); // Always starts from 1
};
Why It Happens:
React re-initializes variables on every render. For persistent values, use useRef:
const countRef = useRef(0);
const increment = () => {
countRef.current += 1;
console.log(countRef.current); // Works as expected
};
Closing Thoughts
When you know the "why," youâre not just reacting â youâre in control. React is amazing, but it can be a little confusing at times. Understanding why things work the way they do makes it way less frustrating. If you get why it's acting up, youâll save a lot of time and frustration. Itâs all part of its unique (and sometimes confusing) charm.
Featured ones: