1

I'm currently learning the react hooks with an online course.

The instructor passed an anonymous callback function onto the onClick handler

  return (
    <div className="counter">
      <button className="counter-action decrement" onClick={() => decrementScore()}> - </button>
      <span className="counter-score">{score}</span>
      <button className="counter-action increment" onClick={() => incrementScore()}> + </button>
    </div>
  );

But I don't understand why the anonymous callback is needed, and why I can't just pass the function by itself.

Following is what I tried and it worked ok without an error.


const Counter = () => {
  const [score, setScore] = React.useState(0);

  const incrementScore = () => {
    setScore(prevScore => prevScore + 1);
  }

  const decrementScore = () => {
    setScore(prevScore => prevScore > 0 ? prevScore - 1 : 0);
  }

  return (
    <div className="counter">
      <button className="counter-action decrement" onClick={decrementScore}> - </button>
      <span className="counter-score">{score}</span>
      <button className="counter-action increment" onClick={incrementScore}> + </button>
    </div>
  );
}
skyboyer
  • 22,209
  • 7
  • 57
  • 64
Suan
  • 11
  • 2

1 Answers1

3

The additional anonymous callback isn't needed here. Doing it your way is fine.

It would be useful to have such an anonymous callback if:

  • The function being called was a member of an object, and it needs a this context of the object, and the function isn't bound. For example, the following would look pretty suspicious in a class component:

    onClick={this.handleClick}
    

    (see here for many pages on the subject)

  • You want to make sure the function is called with a particular argument or arguments, and not with others. For example, you might have

    <button onClick={() => changeValue(1)}>click1</button>
    <button onClick={() => changeValue(2)}>click 2</button>
    

    Or, you might possibly want to omit the default React event that gets passed as the first argument to an event handler. But, in this case, no arguments are used in incrementScore or decrementScore, so it doesn't matter.

Additionally, note that you only need to use the callback form of a state setter, eg:

setScore(prevScore => prevScore + 1);

when the state value that the enclosing function closes over may be stale - if the state setter has been called previously, and the component hasn't re-rendered yet. But for a state that changes only once when a button is clicked, the state value has no chance of being stale. So, if you wanted, in this case, you could simplify

const incrementScore = () => {
  setScore(prevScore => prevScore + 1);
}

to

const incrementScore = () => {
  setScore(score + 1);
}
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Oh I see! Is doing it my way ok because it returns another function, not just a value? And also is it still more practical to use anonymous function? Thanks so much for your help – Suan Nov 25 '22 at 23:37
  • Both `() => decrementScore()` and `decrementScore` resolve to functions that, when called, call a state setter as desired. I'd consider an unnecessary wrapping anonymous function a slight anti-pattern. – CertainPerformance Nov 25 '22 at 23:38
  • Thank you so much (I also edited the question to anonymous function to be more accurate, thanks to you) – Suan Nov 25 '22 at 23:43
  • Oh! And I used prevScore as the score changes based on the previous score, every time the button is clicked. – Suan Nov 25 '22 at 23:47
  • The callback is useful only when you've previously called the state setter *before a re-render has occurred*. Since the component is re-rendering every time there's a click, and you're only calling the state setter once when there's a click, feel free to omit the callback form if you wish. – CertainPerformance Nov 25 '22 at 23:49
  • I finally understood what you explained above after googling the basic terms for 15 mins a long way to go! Thanks again – Suan Nov 26 '22 at 00:09
  • @Suan I was reading about this a few weeks ago :) , this was helpful also: https://beta.reactjs.org/learn/queueing-a-series-of-state-updates – theCJCsoccer Nov 26 '22 at 00:42
  • I just read the docs and did some of the exercises, and now I have a better understanding of the state updates. I love how they explained things beginner-friendly with examples! Thank you so much x – Suan Nov 26 '22 at 04:51