mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
ee38751975
Summary: Under Fabric only, we can enter an infinite layout loop where the emitted layout event oscillates between two height values that are off by a very small amount. The cause is, in part, components that use layoutEvents to determine their dimensions: for example, using onLayout event "height" parameters to determine the height of a child. If the onLayout height changes rapidly, the child's height will change, causing another layout, ad infinitum. This might seem like an extreme case but there are product use-cases where this is done in non-Fabric and layout stabilizes quickly. In Fabric, currently it may never stabilize. Part of this is due to a longstanding issue that exists in Fabric and non-Fabric, that we cannot immediately fix: If in a single frame, C++ emits 100 layout events to ReactJS, ReactJS may only process 50 before committing the root. That will trigger more layout events, even though product code has only partially processed the layout events. At the next frame, the next 50 events will be processed which may again change the layout, emitting more events... etc. In most cases the tree will converge and layout values will stabilize, but in extreme cases in Fabric, it might not. Part of this is because Fabric does not drop *stale* layout events. If 10 layout events are dispatched to the same node, it will process all 10 events in older. Non-Fabric does not have this behavior, so we're changing Fabric to drop stale events when they queue up. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D23719494 fbshipit-source-id: e44a3b3e40585b59680299db3a4efdc63cdf0de8
78 lines
2.4 KiB
C++
78 lines
2.4 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 {
|
|
// Due to State Reconciliation, `onLayout` can be called potentially many
|
|
// times with identical layoutMetrics. Ensure that the JS event is only
|
|
// dispatched when the value changes.
|
|
{
|
|
std::lock_guard<std::mutex> guard(layoutMetricsMutex_);
|
|
if (lastLayoutMetrics_ == layoutMetrics) {
|
|
return;
|
|
}
|
|
lastLayoutMetrics_ = layoutMetrics;
|
|
}
|
|
|
|
std::atomic_uint_fast8_t *eventCounter = &eventCounter_;
|
|
uint_fast8_t expectedEventCount = ++*eventCounter;
|
|
|
|
// dispatchUniqueEvent only drops consecutive onLayout events to the same
|
|
// node. We want to drop *any* unprocessed onLayout events when there's a
|
|
// newer one.
|
|
dispatchEvent(
|
|
"layout",
|
|
[frame = layoutMetrics.frame, expectedEventCount, eventCounter](
|
|
jsi::Runtime &runtime) {
|
|
uint_fast8_t actualEventCount = eventCounter->load();
|
|
if (expectedEventCount != actualEventCount) {
|
|
// Drop stale events
|
|
return jsi::Value::null();
|
|
}
|
|
|
|
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
|