Should React use requestAnimationFrame by default?
See original GitHub issueConsider the following sample code: (pasted here too)
class extends React.Component {
private canRender: boolean = false;
private latestData: any;
constructor(props) {
super(props);
let nJobs = 0;
let lastRenderTime: number;
props.someObservableThing.listen(data => {
nJobs++;
this.latestData = data;
if (this.canRender) {
const now = performance.now();
this.canRender = false;
this.setState({
data: this.latestData,
jobsPerRender: nJobs,
fps: (lastRenderTime === undefined) ? 0 : 1000 / (now - lastRenderTime)
});
nJobs = 0;
lastRenderTime = now;
}
});
this.state = {};
}
/* Lifecycle */
componentDidMount() {
this.canRender = true;
}
componentDidUpdate() {
this.canRender = true;
}
render() {
outputStats(this.state);
return this.state.data === undefined ? null : <View {...this.state.data} />
}
}
When outputStats is hit - I’m getting framerates of like 2000fps. In other words requestAnimationFrame
does not seem to be a limiter for react itself.
Is this correct?
(as a slightly separate topic- if that is true, for animation things do you think it would be good to simply wrap the if (this.canRender) {}
block in a requestAnimationFrame()
? I guess that’s not really a React question though since the observableThing could also be capped via ticks…)
Issue Analytics
- State:
- Created 6 years ago
- Reactions:1
- Comments:37 (17 by maintainers)
Top Results From Across the Web
How to Use requestAnimationFrame with React | Pluralsight
This guide covers the use of the requestAnimationFrame method to create optimal animations in react. The codebase is available on github.
Read more >Does React use requestAnimationFrame? If so, how does it ...
React doesn't currently use requestAnimationFrame to do DOM updates (as we call it, the "batching strategy"). The batching strategy is ...
Read more >Using requestAnimationFrame in ReactJS | by Savita Patel
The requestAnimationFrame() tells the browser that you wish to perform an animation and requests that the browser calls a specified function to update...
Read more >requestAnimationFrame and useEffect vs useLayoutEffect
While trying to implement an animated number counter I stumbled upon an interesting issue with useEffect and requestAnimationFrame not playing ...
Read more >Stop Using setInterval. Use requestAnimationFrame
The reason for this is because you must re-call requestAnimationFrame inside your function to queue up the next time the function will be...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
I don’t think that’s what Sebastian is saying. I think what he’s saying is that it’s important for the result of every click event to flush before the next click event. Consider a button that sets
disabled={this.state.hasSent}
andonClick={this.state.hasSent ? null : this.sendMoney}
. IfsendMoney
setshasSent
totrue
, the product developer expectation is thatonClick
will not fire again. Otherwise two fast clicks can send the money twice, and that is a very error-prone conceptual model.This is why we think it is important to treat
setState
inside interactive events (such as clicks) differently fromsetState
inside, for example, network responses. And my understanding is that our plan is to flush “interactive” updates (like clicks) at the end of the event, but flush “other” updates (including network) with a low priority, chunked overrIC
.Andrew’s earlier point was that once we switch over to that approach, using
setState
for animation will not be practical by default because React will only flush it once a second or so. To fix this, the user would need to move theirsetState
calls into a special call that flushes immediately (calledflushSync
). This “opts in” asetState
into being processed synchronously. Since it is important when this happens we think it would be user’s responsibility to put that call inside arAF
. However we don’t exclude providing some hook to coordinate this with other libraries.To sum up:
setState
inside of interactive events (such as click) acting like now, with flushing at browser event boundary.setState
calls to low priority, meaning that if it comes from a network request or some non-interactive event, it will be processed separately, split overrIC
and flushed within a second or two.flushSync(fn)
method as an opt out of async-by-default behavior. IfsetState
is called inside offn
it is flushed right after it exits. This is an important escape hatch for some scenarios where you really need to manually flush the update before React considers it necessary. For example if you’re in a click handler and you want to measure a DOM node after asetState
and use that information for final rendering result.setState
low-priority outside of events we decided must flush (like clicks) will “break” users who use it for animation (because their updates won’t be flushed inside events likeonMouseMove
—the exact scenario you’re not happy with). As a migration path we will ask them to call theirsetState
insideflushState
in arequestAnimationFrame
callback. We don’t want to “own” that callback because we’re not the only ones who might want to use it, and therefore the user should be in control of it.Does this help at all? I might have made a mistake somewhere so I’ll ask Andrew and Seb to verify, but this is my understanding.
The fifth point is not a final decision, we just haven’t gotten to it yet. We might use a strategy that’s a bit more obvious to the user or less error-prone. The intended takeaway is that: (1) I don’t see how we could use rAFs for clicks, (2) we already have a more efficient strategy in the work for non-clicks (rIC) so adding rAF in the mix doesn’t make it better, (3) we’ll need to think more about animation-specific use case, but it will likely build on top of the more flexible flushSync promitive we’ll have to expose anyway, (4) the user won’t get over-rendering animation by default as you’re worried, instead the animation will flush too rarely, leading them to research the right rAF solution (which we will document or provide a convenience API for).
FWIW we’ve since stopped using
requestIdleCallback
because it’s not as aggressive as we need. Instead we use a polyfill that’s internally implemented on top ofrequestAnimationFrame
. Although conceptually we still use it to do idle work.