react-player icon indicating copy to clipboard operation
react-player copied to clipboard

Hooks / React.useState cause flicker issue when used in onProgress

Open cbhoweth opened this issue 4 years ago • 11 comments

Current Behavior

v2 - Using hooks along with state within onProgress callback creates a re-render loop. Player flickers endlessly.

Expected Behavior

Setting a separate piece of state that the player is not dependent on should not re render ReactPlayer

Steps to Reproduce

  1. Create a functional component to use ReactPlayer in
  2. Add a piece of state using hooks pattern ex. const [played, setPlayed] = React.useState(false);
  3. Create a handler function that updates state base on object passed ex.
const handleProgress = (playedObject) => {
    if (!seeking) {
      setPlayed(playedObject.played);
    }
  };
  1. Use handler inside onProgress method on ReactPlayer ex. onProgress={handleProgress}

Example provided: Uncomment setPlayed inside handleProgress to see issue https://codesandbox.io/s/strange-sun-cw9oq?file=/src/App.js:301-311

Environment

  • URL attempting to play: any (example uses https://res.cloudinary.com/filmsupply/video/upload/ac_none,vc_auto/v1606326468/static/Home/home-hero.mp4)
  • Browser: Chrome
  • Operating system: macOS Big Sur
  • Codesanbox example: https://codesandbox.io/s/strange-sun-cw9oq?file=/src/App.js:301-311

Other Information

If we convert this to a class component or revert to v1 the issue is not present. Also state must be set to the passed value on the progress handler (using a static value to set state does not cause flicker)

cbhoweth avatar Oct 07 '21 19:10 cbhoweth

I have the exact same issue. I've found that this only occurs with nested functions. Given the time between updates on this project, I don't have much fate in this being resolved anytime soon.

Therefore, a workaround is putting the player directly in the return statement in the export default function of a certain page. I'm using Next.js (which has a React-based front-end), and this seems to work.

However, it's still a bug. I'd like to hear from someone about a solution that isn't a workaround like this!

Edit: by putting the player directly in the return statement of a default function, I mean the literal <ReactPlayer/> component.

wouter-deen avatar Oct 12 '21 18:10 wouter-deen

This has to be due to the lack of memoization. Any state updates will cause the player to re-render. Using refs to control playback wouldn't have had this effect.

In using functional components to toggle the prop passed to the player element, we'll probably want to use memoization to make sure the player only re-renders when significant pieces of state change.

I'm looking into this now, and it may be that there will need to be changes to the react player api itself, or it could be just an implementation detail.

LongLiveCHIEF avatar Oct 22 '21 17:10 LongLiveCHIEF

can you also let me know which player you guys are using?

LongLiveCHIEF avatar Oct 22 '21 17:10 LongLiveCHIEF

For me, it ended up being cleaner to just have a one return statement with some conditional rendering inside. That also solved the problem of it flickering because it was inside a nested function's return statement. I'm using react-player to play video files stored in a GCP storage bucket currently. I'm using the useState React hook for states like playing, started, progress etc.

wouter-deen avatar Oct 22 '21 17:10 wouter-deen

Uncomment setPlayed inside handleProgress to see issue https://codesandbox.io/s/strange-sun-cw9oq?file=/src/App.js:301-311

Notice the flickering stops when you change url={['file.mp4']} to url='file.mp4'. ReactPlayer is re-rendering because url={['file.mp4']} passes in a newly instantiated array as the url every time your state updates.

I can take a closer look when I get more time. Spare time is not something I have much of at the moment.

cookpete avatar Oct 25 '21 13:10 cookpete

@LongLiveCHIEF we were upgrading to the same version in the example 2.9.0 and using the file player. @wouter-deen thanks for the suggestion we will give that a shot. @cookpete Thanks for looking into it man!

cbhoweth avatar Oct 25 '21 13:10 cbhoweth

I can take a closer look when I get more time. Spare time is not something I have much of at the moment.

My FT job is working with this quite a bit ATM, and this is one that falls in scope, so I can def help.

It seems like we can update the title of this one to indicate only the file player is affected?

Notice the flickering stops when you change url={['file.mp4']} to url='file.mp4'. ReactPlayer is re-rendering because url={['file.mp4']} passes in a newly instantiated array as the url every time your state updates.

In this case, then we can advise user to use useMemo:

const sourceFiles = ['file.mp4', 'another.mp4']
const files = useMemo(() => {[...sourceFiles]}, [sourceFiles])

return (
  <>
    <ReactPlayer
      url={files}
    />
  </>
)

LongLiveCHIEF avatar Oct 25 '21 20:10 LongLiveCHIEF

Yep, or just tell ReactPlayer not to re-render or reload if the url is an array and the items have not changed. Maybe.

cookpete avatar Oct 25 '21 20:10 cookpete

I might be missing something here, but my url prop was literally just url="fileurl.mp4". It had nothing to do with an array and the player would still immediately flicker upon preloading.

wouter-deen avatar Oct 25 '21 20:10 wouter-deen

@wouter-deen If you can create something that reproduces the issue, I can take a look.

cookpete avatar Oct 25 '21 20:10 cookpete

I rewrote my code to use conditional rendering, hence fixing the issue. I don't have any code sample, but I think it had to do with the fact that I used the useState hook to save playing, volume, pip, timestamp etc. states.

For anyone having this issue, I suggest you try to do what I said in my first comment in this thread. If that doesn't resolve the issue, I suggest you work out what the issue is by simply eliminating certain parts (like hooks, fetching the last timestamp from your database etc.) until the problem doesn't pop up.

wouter-deen avatar Oct 29 '21 19:10 wouter-deen

@wouter-deen Can you be more about how you solved it?

any other solutions?

ferrero1987 avatar Sep 23 '22 16:09 ferrero1987

@ferrero1987 Do you have a reproducible example of this happening? Does anyone?

cookpete avatar Sep 26 '22 09:09 cookpete