Search code examples
reactjscallbackreact-props

How to pass callback as a prop to lower components in React without creating a new callback each time?


I am coming form angular and getting used to react. I was reading the docs on event handling and stumbled upon this:

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // This syntax ensures `this` is bound within handleClick
    return (
      <button onClick={() => this.handleClick()}>
        Click me
      </button>
    );
  }
}

the docs then say:

" The problem with this syntax is that a different callback is created each time the LoggingButton renders. In most cases, this is fine. However, if this callback is passed as a prop to lower components, those components might do an extra re-rendering. We generally recommend binding in the constructor or using the class fields syntax, to avoid this sort of performance problem."

I don't have access to constructor as I am using hooks. What should I do?

I prepared a quick demo. The structure is App > [CompA > Comp B]. There is a button in component B that increments count and the state of the count is kept in App. Through props the callback is delegated from App through Comp A to comp B. Whenever I click the count button in Comp B, it re-renders the entire Comp A. This can be verified by the random number generation. It's fine for this component but wouldn't this be a problem in a large project?

Now imagine I have a quiz app and I keep score in the main App. If someone selects right answer inside a Question component I would like to keep a count of that in the main App. Doing it this way would re-render any intermediate components. What's the pattern I should follow?


Solution

  • Use the useCallback hook. It'll return the same function as long as its dependencies remain the same.

    const callback = useCallback(()=> { /* do something */ }, [/* dependencies */])

    The dependencies work the same as the useEffect hook.

    Edit

    From your demo, <CompA onCount={() => setCount(count + 1)} /> is perfectly fine, Dan Abramov said as much.

    Parent re-renders will render their children, so if the count state is in the parent, the children will be re-rendered. That's perfectly fine. React is built to do that very quickly.