Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using requestAnimationFrame in React

I am new to react native and I trying to optimize performance.

My Touch events are very slow and I was going through RN documentation on performance and they have mentioned to use requestAnimationFrame with this example

handleOnPress() {
  // Always use TimerMixin with requestAnimationFrame, setTimeout and
  // setInterval
  this.requestAnimationFrame(() => {
    this.doExpensiveAction();
  });
}

Now, this description sounds very vague and hard for me to comprehend its usage

Like, I have this touchable event in my RN app

<TouchableWithoutFeedback   onPress={() => this.touched(this.props.coinShortName)}>

Which calls this method

 touched = (id) => {

        this.setState({selectedPostId: id})
        if (this.props.coinShortName == this.state.selectedPostId ) { 
           this.setState({stateToDisplay: !this.state.stateToDisplay})
        }
    }

Question: Can someone please tell me on how I need to/should use it in my app?

like image 808
iRohitBhatia Avatar asked Sep 06 '18 22:09

iRohitBhatia


People also ask

Does react use requestAnimationFrame?

React doesn't currently use requestAnimationFrame to do DOM updates (as we call it, the "batching strategy"). The batching strategy is injectible though so it's possible to use something else.

When should you use requestAnimationFrame?

requestAnimationFrame() is 1 shot. You should call this method whenever you're ready to update your animation onscreen. This will request that your animation function be called before the browser performs the next repaint.

Is requestAnimationFrame asynchronous?

As now you know that the rAF is a Web API, that means the callback will be called asynchronously. Unlike setInterval , requestAnimationFrame does not accept delay argument, instead, it only calls the callback function when the browser is ready to perform the next paint operation.


2 Answers

I'm going to convert my comment into an answer, so I can format it better and hopefully help someone in the future too. I don't expressly recommend marking my answer as correct, but I think this answer should be here alongside this question.

This article here should give you some backstory on requestAnimationFrame: http://www.javascriptkit.com/javatutors/requestanimationframe.shtml.

I would recommend reading the article I linked above and then read my answer after.

I will just explicitly mention that requestAnimationFrame could appear similar to setTimeout(() => {}, 0), but if you had a Zack Morris phone made in 1985, its "as soon as possible" might be 5 seconds later, thus making your animation look terrible, similar to when your character lags across the screen in a video game. The function may have been called at the correct time, but it did not actually execute at the correct time.

It can be helpful to imagine a collection phase and a rendering phase. I'm sorry, I don't know the exact terms for this stuff, but human eyes see smooth movement at I think 20 FPS, but what that means is you have 20 "frames", so it's like calculating something 20 times. It's like collecting a bunch of kids and pushing them into a bus 20 times per second. Pushing them into the bus is an event, and it's analogous to repainting your screen. Sometimes kids can get left behind and extra kids picked up next time, so you can imagine the gains to perceived smoothness of flow over time.

It's important to note that optimizations are being made with respect to the next repaint, or the next time the screen 'changes'. requestAnimationFrame does work under the hood to ensure the animation occurs at the correct time and is smooth, meaning the pixels were where they were supposed to be at the right time. (I think you would derive a lot of meaning if you checked out the definitions for "what is a janky animation", and look at some of the discussion around that. I mention that because we want to understand more about the repainting process and what kinds of things are important and why)

I recall that requestAnimationFrame can ditch calculations that would occur too late. For example, if you click the button and a pixel goes from 0% to 25% to 50% to 75% to 100% (some arbitrary distance calculation). We could say that after 1 second, the pixel should have travelled 50% of the distance and after 2 seconds, it should be at 100%, the final resting place.

It's more important that the pixels are in the correct place at the correct time than it is for them to travel to exactly every place they were supposed to. requestAnimationFrame is helping you do this. If the screen is about to repaint, and "it" needs to run a calculation that would take too long, "it" just ignores it and skips to the next frame. It's like trimming fat to keep on pace and therefore, avoid jank.

requestAnimationFrame is a solution for the same challenges whether it's in your web browser or iOS or Android. They all do this process of painting the screen over and over again. You could start calculating something needed for the next repaint but start too late so it's not done when the next repaint occurs.

Imagine your animation was smooth but your phone received 20 push notifications all of a sudden that bogged down the CPU, causing your animation to be delayed by 16.7 milliseconds. Rather than display the pixel at the correct place at the wrong time, requestAnimationFrame helps by making the pixel be in the correct place at the correct time, but it may do some magic and not even try to paint the pixel sometimes when it would have otherwise, thus saving performance and increasing perceived smoothness.

I just realized this is a wall of text, but I think it will be informational.

These repaints occur about 60 frames per second, so requestAnimationFrame could fire like 60 times a second when it calculates is the most optimal time. There are 1000 milliseconds in 1 second, so 60 FPS is one frame every 16.7ms. If the human eye perceives smoothness at 20FPS, then it means you could in theory repaint every 45ms or 30% as much, and the animation would still be smooth.

My definitions may be inaccurate, but I hope they can help give you a sense what is happening.

like image 80
agm1984 Avatar answered Oct 03 '22 08:10

agm1984


As mentioned , you need to wrap your expensive action in an instance of requestAnimationFrame.

Usage

<TouchableWithoutFeedback onPress={() => this.touched(this.props.coinShortName)}>

touched = (id) => {
  requestAnimationFrame(() => {
    this.setState({selectedPostId: id})
    if (this.props.coinShortName == this.state.selectedPostId ) {
     this.setState({stateToDisplay: !this.state.stateToDisplay})
    }
  });
}
like image 26
Pritish Vaidya Avatar answered Oct 03 '22 08:10

Pritish Vaidya