Files
react-native/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp
T
Valentin Shergin 934275f931 Fabric: Changeing debouncing logic of onLayout event
Summary:
This changes the way we throttle `onLayout` events in Fabric.

The approach we used before has several issues:
* Every event-dispatching action initiated a lambda scheduled on JavaScript thread (which is a bit inefficient).
* If an event had {0,0,0,0} frame, it might be skipped because this is the default frame value.
* An event was always delivered by the exact block scheduled at the moment of the event initiation (even though some other blocks might be called before). In case of events being initiated rapidly, it can delay actual event delivery and maybe even overwhelm the JavaScript thread.

The new implementation uses a different approach: we maintain the shared storage with recent frame value and use the very first opportunity to deliver it. Alse see comments in the code.

Changelog: [Internal] Fabric-specific internal change.

Reviewed By: sammy-SC

Differential Revision: D25676336

fbshipit-source-id: 275b08990f7c5cf1f05a8f954ebc795a14e10ec2
2021-01-04 15:02:32 -08:00

117 lines
3.7 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ViewEventEmitter.h"
namespace facebook {
namespace react {
#pragma mark - Accessibility
void ViewEventEmitter::onAccessibilityAction(const std::string &name) const {
dispatchEvent("accessibilityAction", [name](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "action", name);
return payload;
});
}
void ViewEventEmitter::onAccessibilityTap() const {
dispatchEvent("accessibilityTap");
}
void ViewEventEmitter::onAccessibilityMagicTap() const {
dispatchEvent("magicTap");
}
void ViewEventEmitter::onAccessibilityEscape() const {
dispatchEvent("accessibilityEscape");
}
#pragma mark - Layout
void ViewEventEmitter::onLayout(const LayoutMetrics &layoutMetrics) const {
// A copy of a shared pointer (`layoutEventState_`) establishes shared
// ownership that will be captured by lambda.
auto layoutEventState = layoutEventState_;
// Dispatched `frame` values to JavaScript thread are throttled here.
// Basic ideas:
// - Scheduling a lambda with some value that already was dispatched, does
// nothing.
// - If some lambda is already in flight, we don't schedule another;
// - When a lambda is being executed on the JavaScript thread, the *most
// recent* `frame` value is used (not the value that was current at the
// moment of scheduling the lambda).
//
// This implies the following caveats:
// - Some events can be skipped;
// - When values change rapidly, even events with different values
// can be skipped (only the very last will be delivered).
// - Ordering is preserved.
{
std::lock_guard<std::mutex> guard(layoutEventState->mutex);
// If a *particular* `frame` was already dispatched to the JavaScript side,
// no other work is required.
if (layoutEventState->frame == layoutMetrics.frame &&
layoutEventState->wasDispatched) {
return;
}
// If the *particular* `frame` was not already dispatched *or*
// some *other* `frame` was dispatched before,
// we need to schedule the dispatching.
layoutEventState->wasDispatched = false;
layoutEventState->frame = layoutMetrics.frame;
// Something is already in flight, dispatching another event is not
// required.
if (layoutEventState->isDispatching) {
return;
}
layoutEventState->isDispatching = true;
}
dispatchEvent("layout", [layoutEventState](jsi::Runtime &runtime) {
auto frame = Rect{};
{
std::lock_guard<std::mutex> guard(layoutEventState->mutex);
layoutEventState->isDispatching = false;
// If some *particular* `frame` was already dispatched before,
// and since then there were no other new values of the `frame` observed,
// do nothing.
if (layoutEventState->wasDispatched) {
return jsi::Value::null();
}
frame = layoutEventState->frame;
// If some *particular* `frame` was *not* already dispatched before,
// it's time to dispatch it and mark as dispatched.
layoutEventState->wasDispatched = true;
}
auto layout = jsi::Object(runtime);
layout.setProperty(runtime, "x", frame.origin.x);
layout.setProperty(runtime, "y", frame.origin.y);
layout.setProperty(runtime, "width", frame.size.width);
layout.setProperty(runtime, "height", frame.size.height);
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "layout", std::move(layout));
return jsi::Value(std::move(payload));
});
}
} // namespace react
} // namespace facebook