Search code examples
reactjsreact-hooksreact-functional-componentrerenderusecallback

how to avoid rerender due to useCallback dependency change


I have a VideoPlayer component with crop functionality that's not working well after migration to functional over class component.

The VideoPlayer has a isVideoPlaying state (useState). It also contains a function toggleOrChangeIsVideoPlaying:

const togglePlayPauseVideo = (toPlay) => {
  if (toPlay !== undefined) {
    if (toPlay) {
      playVideo()
    } else {
      pauseVideo()
    }
  } else {
    if (!isVideoPlaying) {
      playVideo()
    } else {
      pauseVideo()
    }
  }
}

It renders:

<div>
  <Crop onPlayPauseVideo={togglePlayPauseVideo} ...restofTheProps>
    <Video ...someProps />
  </Crop>
</div>

Using useEffect & console.log() I verified that 'togglePlayPauseVideo' function is changing (and causing a bug), probably because VideoPlayer is re-rendered.

I've tried wrapping 'togglePlayPauseVideo' with useCallback. The problem is that it must have 'isVideoPlaying' state as a dependency (otherwise there's another bug), but when it does, it changes again more than it should.

Any ideas how to break this cycle?

BTW 1: 'isVideoPlaying' state is needed to keep track of the actual element state, that changes in playVideo() and pauseVideo() via ref.

BTW 2: VideoPlayer worked ok when it was a class component.


Solution

  • Found the answer myself, posting to help others that will see this.

    Of course there was some bad code there, but I'm working mainly on migrating class components to functional components, so I didn't want to do too much refactoring. But this time it was needed. The toggle function is actually 3 functions. toggle, play & pause. I this specific design, it caused a dependency circle. All I did was to break this function into 3 separate ones and it all worked. Maybe if the "code smell" is so obvious we should try to always refactor and don't leave it for later.

    Thanks for the help :)