We’ve all written code like this before:
const [value, setValue] = useState(getInitialValue());
It looks innocent — but this line hides a subtle performance pitfall.
When you call useState(getInitialValue()), that function executes on every render, not just the first one.
That’s because React re-runs your component function on every render, and the argument to useState() is evaluated before React decides whether to reuse the existing state.
If getInitialValue() does something expensive — like computing, parsing, or reading from storage — you’re wasting CPU cycles on every render.
function getInitialValue() {
console.log("getInitialValue() called");
// imagine something slow here...
return Math.random();
}
function ExampleComponent() {
const [value, setValue] = useState(getInitialValue);
return (
<div>
<p>Value: {value}</p>
<button onClick={() => setValue(v => v + 1)}>Increment</button>
</div>
);
}
If you open the console, you’ll see:
getInitialValue() called
…only once.
Now try this version instead:
const [value, setValue] = useState(getInitialValue());
Every render prints the log again 😬 — even if value never changes.
The Fix — Lazy Initialization
React has a built-in way to avoid this: pass a function to useState, not the result of a function call.
const [value, setValue] = useState(() => getInitialValue());
On later renders, React reuses the same state value, skipping the function execution entirely.
💡 Tiny tweaks like this don’t just improve performance — they also make your intent clearer to everyone reading your code.
Top comments (0)