DEV Community

Cover image for React Tip: Pass a Function to useState, Don’t Call It
Shahab Emami
Shahab Emami

Posted on

React Tip: Pass a Function to useState, Don’t Call It

We’ve all written code like this before:

const [value, setValue] = useState(getInitialValue());
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

If you open the console, you’ll see:
getInitialValue() called

…only once.

Now try this version instead:

const [value, setValue] = useState(getInitialValue());

Enter fullscreen mode Exit fullscreen mode

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());

Enter fullscreen mode Exit fullscreen mode

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)