React - why use anonymous function for event handler?

I’m wondering if someone could explain why we need to wrap function calls inside anonymous functions when calling the function inside an event handler (see example below)

function Counter() {
  const [count, setCount] = React.useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Because if you won’t wrap, then the function will be called on component mount.
You can omit wrapping if you don’t need to pass custom args to the function:

// will be called with click event as the argument
<button onClick={setCount}>
1 Like

All JavaScript event handlers use an anonymous callback function, this isn’t a React-specific thing: JS uses callbacks to allow you to do one thing (pick up a click event for example) then do another thing (fire a handler function). React follows the exact same pattern, as it’s how the language APIs work:

element.addEventListener("click", () => doThing())

The event handler takes a function, not the value of a function. When the event occurs, the function gets called.

onClick={() => setCount(count + 1)}

When the button gets clicked, execute that function. When that function gets executed, setCount is ran.

If you wrote it like this:

onClick={setCount(count + 1)}

That isn’t a function, it’s the result of a function (which in this case is undefined, which is not an executable thing).

The callback function optionally takes an argument, which is the Event object created when the click event happens:

onClick={(event) => setCount(count + 1)}

You aren’t using that event object here, so you can just ignore it


Re the removal of the wrapping function: this isn’t anything special. The event takes a callback function, so you can pass any function you want. But if you did this:

onClick={setCount}

That’s passing a function as the handler (which is fine!), but it’ll do the same as this:

onClick={(event) => setCount(event)}

Which is not fine, as it’ll set count to be the event object.

4 Likes

Thanks for such a detailed response! :smiley:

I’m just wondering why the event object would be passed in as a parameter to setCount, when we’re not explicitly calling it, but just passing the setCount function into the handler.
As you mentioned previously that event object is an optional argument in the callback function.

The callback function always passes that in as an argument – you don’t need to use it, but it’s always there.

document.addEventListener("click", (event) => {
  // do something with the event data
  // or do something else, but event data
  // is always there if you need it
});

And then it’s just a process of simplification, very similar to algebra. If I remember correctly, it’s called beta reduction. Say you have a function like this:

function outer () {
  return inner();
}

Calling outer is exactly the same as calling inner. There is no difference. So you don’t need the outer function, you can reduce it to just inner.

Say it takes an argument:

function outer(a) {
  return inner(a);
}

Again, calling outer is exactly the same as calling inner, the function outer can just be reduced to inner.

But if the arguments are not the same, you can’t reduce it:

function outer(a) {
  return inner(b);
}

Calling outer may not produce the same result as calling inner, and if that was reduced to just inner you’d end up with a being passed to inner, which would be incorrect.

That’s a bit abstract, so to make it more concrete:

function eventHandler (event) {
  return myHandlerFunction(event);
}

So calling myHandlerFunction is exactly the same as calling myHandlerFunction, whereas:

function eventHandler (event) {
  return myHandlerFunction(doSomething);
}

These aren’t the same

1 Like

If you just used onClick={setCount(count + 1)} then setCount would be called on the render rather than the click, which would give you an infinite loop because a state update will cause another render.

You could create a function just before return:

function buttonClicked() {
setCount(count + 1)
}

And then use that in the button:

<button onClick={buttonClicked}>

I like this approach sometimes as you can then give the function a nice name. Especially if there are a few lines of code it is nice to separate it out.

1 Like

This is the best and most comprehensive explanation I didn’t read in ages. Congrats!