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>
  );
}
0

1 Answer 1

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);
}
Sign up to request clarification or add additional context in comments.

8 Comments

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 🙏
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.
Thank you so much 😊 (I also edited the question to anonymous function to be more accurate, thanks to you😉)
Oh! And I used prevScore as the score changes based on the previous score, every time the button is clicked.
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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.