Introduce EventBeat::unstable_setInduceCallback to batch sync state updates (#49520)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49520

changelog: [internal]

Adds a new callback to EventBeat that is triggered in EventBeat::induce (that is when the UI thread is about to go IDLE). This will make state updates applied synchronously before the OS has a chance to paint but they will be batched. If two state updates happen in single UI tick, the will be executed at the end of UI's run loop.

The state updates are still not batched into a single commit. If two state updates happen on the same component, they are coalesced. If two state updates happen on two different components, they will generate two transactions. This is a follow up optimisation.

This feature is hidden behind `ReactNativeFeatureFlags::enableSynchronousStateUpdates` which is a preexisting feature flag. The feature flag was not rolled out yet and is not being used in any QEs at the moment.

Reviewed By: rubennorte

Differential Revision: D69848997

fbshipit-source-id: 870fd21f1d9d73d84861ddeac95b932a7fe1f5fe
This commit is contained in:
Samuel Susla
2025-02-19 07:10:22 -08:00
committed by Facebook GitHub Bot
parent e5f08c7ae0
commit 236db59a26
4 changed files with 27 additions and 7 deletions
@@ -37,11 +37,19 @@ void EventBeat::setBeatCallback(BeatCallback beatCallback) {
beatCallback_ = std::move(beatCallback);
}
void EventBeat::unstable_setInduceCallback(std::function<void()> callback) {
induceCallback_ = std::move(callback);
}
void EventBeat::induce() const {
if (!isEventBeatRequested_) {
return;
}
if (induceCallback_) {
induceCallback_();
}
isEventBeatRequested_ = false;
if (isBeatCallbackScheduled_) {
@@ -121,6 +121,15 @@ class EventBeat {
*/
void setBeatCallback(BeatCallback beatCallback);
/*
* The callback will be executed once a consumer (for example EventQueue)
* calls either `EventBeat::request` or `EventBeat::requestSynchronous`. The
* callback will be executed on the UI thread.
*
* If not set, this is a no-op and callback won't be called.
*/
void unstable_setInduceCallback(std::function<void()> callback);
protected:
/*
* Induces the next beat to happen as soon as possible.
@@ -129,6 +138,7 @@ class EventBeat {
void induce() const;
BeatCallback beatCallback_;
std::function<void()> induceCallback_;
std::shared_ptr<OwnerBox> ownerBox_;
/*
@@ -7,7 +7,6 @@
#include "EventDispatcher.h"
#include <cxxreact/JSExecutor.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/core/StateUpdate.h>
#include "EventQueue.h"
@@ -43,11 +42,7 @@ void EventDispatcher::experimental_flushSync() const {
}
void EventDispatcher::dispatchStateUpdate(StateUpdate&& stateUpdate) const {
if (ReactNativeFeatureFlags::enableSynchronousStateUpdates()) {
statePipe_(stateUpdate);
} else {
eventQueue_.enqueueStateUpdate(std::move(stateUpdate));
}
eventQueue_.enqueueStateUpdate(std::move(stateUpdate));
}
void EventDispatcher::dispatchUniqueEvent(RawEvent&& rawEvent) const {
@@ -7,6 +7,7 @@
#include "EventQueue.h"
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include "EventEmitter.h"
#include "ShadowNodeFamily.h"
@@ -19,6 +20,10 @@ EventQueue::EventQueue(
eventBeat_(std::move(eventBeat)) {
eventBeat_->setBeatCallback(
[this](jsi::Runtime& runtime) { onBeat(runtime); });
if (ReactNativeFeatureFlags::enableSynchronousStateUpdates()) {
eventBeat_->unstable_setInduceCallback([this]() { flushStateUpdates(); });
}
}
void EventQueue::enqueueEvent(RawEvent&& rawEvent) const {
@@ -84,7 +89,9 @@ void EventQueue::experimental_flushSync() const {
}
void EventQueue::onBeat(jsi::Runtime& runtime) const {
flushStateUpdates();
if (!ReactNativeFeatureFlags::enableSynchronousStateUpdates()) {
flushStateUpdates();
}
flushEvents(runtime);
}