React Performance: One Line That Fixes Slow Renders
Stop blocking the main thread! Learn how a simple callback in useState can drastically improve your app performance.
I recently reviewed a codebase where the entire application felt... sluggish. Typing in a simple input field felt sticky. You know that feeling—like the UI is dragging 100ms behind your fingers.
The developer had spent days looking at Memoization, useCallback, and trying to optimize the rendering tree.
But the problem wasn't in the re-renders.
It was in the Native JavaScript Execution.
Here is the exact line that killed the performance:
const [userSettings, setUserSettings] = useState(getComplexSettings());
See it? It looks innocent. We do this every day. But there is a hidden danger in this specific line of code that most React tutorials gloss over.
The "Silent" Execution#
To understand why this is bad, we have to remember how JavaScript works. This isn't even a React rule; it's a JavaScript rule.
Function arguments are evaluated before the function is called.
Look at this plain JS example:
function eat(food) {
// I am not hungry, so I won't eat.
return;
}
// Javascript calculates 'cookSteak()' FIRST, then passes the result to 'eat'.
eat(cookSteak());
Even if the eat function decides to do nothing, cookSteak() has already run. The CPU time is gone.
Now apply this to React:
function UserProfile() {
// React runs this function every time the component re-renders.
// 1. heavyCalculation() RUNS and burns 50ms of CPU.
// 2. React looks at the result and thinks: "Oh, I already have the state from the first render. I'll throw this new value away."
const [data, setData] = useState(heavyCalculation());
return <input />;
}
Every time you type a character in that input, UserProfile re-renders.
And every time it re-renders, heavyCalculation() runs again.
And again.
And again.
If that calculation takes 50ms, and you type "Hello World" (11 chars), you just froze the main thread for half a second purely for work that React immediately threw in the trash.
The Solution: Lazy Initialization#
React has a built-in escape hatch for this. It's called Lazy Initial State.
Instead of passing the value, you pass a function that returns the value.
// BAD 🔴
// Runs on every render.
const [data, setData] = useState(heavyCalculation());
// GOOD 🟢
// Runs only once.
const [data, setData] = useState(() => heavyCalculation());
See that little () => arrow function? That changes everything.
Now, you are passing a function definition to React.
- On First Render: React calls your function, gets the value, and saves it.
- On Re-renders: React sees you passed a function, checks its internal storage, sees it already has the state, and doesn't call your function at all.
A Real-World Villain: localStorage#
You might be thinking, "I don't run heavy math in my components." Do you read from LocalStorage?
// This is synchronous blocking I/O
const [token] = useState(localStorage.getItem('auth-token') || '');
localStorage is fast, but it's not free. Reading from disk (even SSDs) involves serialization and parsing.
If you have a large JSON object in local storage, JSON.parse(localStorage.getItem('config')) can easily take 5-10ms.
Multiply that by 10 inputs on a page updating as you type, and you have noticeable jank.
The Fix:
const [token] = useState(() => {
return localStorage.getItem('auth-token') || '';
});
When should you use this?#
Don't go adding arrow functions to everything. If your initial state is just a primitive, the extra function creation is actually (slightly) slower.
** ✅ Do NOT use for:**
useState(0)
useState('hello')
useState(true)
useState([])
** 🚀 DO use for:**
- localStorage / sessionStorage: Any storage access.
- Date Computations:
new Date()ormoment(). - URL Parsing:
new URLSearchParams(window.location.search). - Heavy Math: Filtering large arrays or finding items.
Summary#
This is one of the easiest performance wins you can get in React. It requires no architectural changes—just six extra keystrokes: () =>.
Next time your input fields feel sticky, check your useState. You might be cooking a steak that nobody is eating.
WebFiddle