React: Function References vs Function Calls
In React code, you may see the following ways of calling functions in jsx elements:
onClick={handleClick}
onClick={() => handleClick()}
Understanding the difference and when to use which pattern is a common point of confusion. Let’s unpack what’s really going on, and what React is doing with the function you give it.
Function Reference vs Function Call
JavaScript has first-class functions, meaning they can be called with () or passed around as variables (without ()). Let’s illustrate this with a simple function:
function sayHi() {
return "hi";
}
sayHi(); // "hi"
// or we can pass a reference to this function into another function:
function greet(fn, name) {
console.log(fn() + " " + name);
}
greet(sayHi, "Joe"); // "hi Joe"
In the greet function, we’ve passed a reference (without parentheses) to the sayHi function. This sayHi function is then called (with parentheses) when the greet function is invoked. The key idea is:
sayHi→ “Here’s the function.”sayHi()→ “Run the function now.”
What React does with onClick
React expects a function reference, not a function call. For example:
<button onClick={sayHi}>Click</button>
React will store the reference to the sayHi function so that later, when the button is clicked, it will run sayHi(event). This is the normal, intended way to tell React to run a function when the button is clicked.
If instead you wrote:
<button onClick={sayHi()}>Click</button>
you would see the sayHi function run immediately when rendered instead of waiting for the click.
Arrow functions
It’s also common to use arrow functions in React event handlers. Let’s illustrate this example using the greet function from earlier:
function greet(name) {
alert(`Hello, ${name}!`);
}
function GreetingExample() {
const [name, setName] = useState("");
return (
<div>
<input
type="text"
value={name}
placeholder="Enter your name"
onChange={(e) => setName(e.target.value)}
/>
<button onClick={() => greet(name)}>Click</button>
</div>
);
}
This function needs to pass an argument, name. But we can’t write it like this:
<button onClick={greet(name)}>Click</button>
because that would call greet(name) immediately during render, not when the user clicks.
The arrow function solves this by giving React a function reference: a small wrapper function that React can call later when the click actually happens. In this pattern, the arrow function creates a new function each render, but that’s perfectly fine for most cases. Here’s how that happens step by step:
React renders the button and stores the arrow function as the click handler.
When the button is clicked, React runs the arrow function.
The arrow function then calls
greet(name)with the latestnamevalue.
Pop quiz moment — what would happen if you tried this:
<button onClick={() => sayHi}>Click</button>
If you thought “nothing,” you are correct! Nothing would happen in this example because the arrow function receives a reference to sayHi, but does NOT invoke it!
Analogy
Think of this like giving React your phone number.
onClick={fn}→ You give React your number. React dials it later.onClick={fn()}→ You dial the phone number right now, and give React the result of the call.onClick={() => fn}→ You give React a note that says “here’s the number" — but the number is never dialed, and React never makes the call.
When to use which
| When to use | Example | Why |
| You don’t need arguments | onClick={handleClick} | Simpler and avoids creating a new function on each render |
| You need arguments or extra logic | onClick={() => handleClick(id)} | Lets you call your function with arguments when clicked |
| ❌ Don’t do this | onClick={handleClick()} | Calls immediately during render |
| ❌ Or this | onClick={() => handleClick} | Returns a function, doesn’t call it |
Wrapping up
React doesn’t magically call functions — it simply calls whatever function reference you hand it when an event occurs. Understanding that difference between a function reference (fn) and a function call (fn()) explains nearly every “why doesn’t my onClick work?” moment.
When in doubt:
Pass a function reference when you just need to call it later.
Use an arrow function when you need to pass arguments or do extra work.
Never call the function directly inside JSX unless you explicitly want it to run during render (rare!).