mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
332355e80f
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/43581 Changelog: [internal] This implements a mechanism to ensure that JavaScript tasks have a consistent view of the state of the UI during their execution. ## Context Fabric allows committing new revisions of the ShadowTree from any thread, but we don't make use of this capability and instead always commit them from the JS thread (e.g.: when we schedule Fabric state updates to update the offset of a list on scroll). This was done to make sure that JS work didn't see changes in the state of the tree at random points during its execution. E.g.: ``` useEffect(() => { const rect = ref.current.getBoundingClientRect(); // do something const newRect = ref.current.getBoundingClientRect(); // `rect` and `newRect` should always be the same }, []); ``` This isn't used by Reanimated at the moment, which means JS can inadvertently see the result of animations in non-specific times during execution. You can find additional context about this in the [RFC for DOM Traversal & Layout APIs in RN](https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0607-dom-traversal-and-layout-apis.md#consistency-and-updates). This works correctly at the moment, but we introduce a limitation in the execution model to prevent updating the tree synchronously from the main thread. One of the main problems this introduces is that computing intersections (for `IntersectionObserver`) relies on the information in the shadow tree, but this is updated asynchronously on scroll. There are 2 potential solutions for that problem: 1) Send the timestamp of the scroll even with the state update to backdate the timestamps of the intersections. This could work but introduces more complexity and possibly accuracy problems due to batching those state updates with other changes (e.g.: what happens if we update the state and commit another tree in the same task? should we use the backdated timestamp or wait for mount?). 2) (**Preferred**/ this diff) Allow committing new revisions from any thread, but lock the JS thread into seeing a specific revision, which would only update/progress in specific moments when it's safe. Some of those moments would be: 1) When we start a new JS task. 2) When we commit a new tree from React (JS). ## Changes This implements the solution outlined in 2), creating a few abstractions to handle what's the current tree that should be visible to JS and to lock/unlock it in specific moments. More specifically: * Creates `ShadowTreeRevisionProvider` as an abstract class for APIs consuming the visible revision of the ShadowTree (mainly DOM APIs and layout methods like `measure`, etc.). * Creates `ShadowTreeRevisionConsistencyManager` as an abstract class to handle what trees are visible (with a `lockRevision` and `unlockRevision` to be called from `RuntimeScheduler` at the beginning and end of each JS task). * Creates 2 different implementations of these abstractions: * One that preserves the current behavior (`LatestShadowTreeRevisionProvider`, which just returns the last committed revision at the time of the call). * One that locks revisions lazily (the first time they're accessed) (`LazyShadowTreeRevisionConsistencyManager`). Reviewed By: sammy-SC Differential Revision: D55024832 fbshipit-source-id: b59985bc83714ae7ec915baba72bf92b3d6fa140
101 lines
3.1 KiB
C++
101 lines
3.1 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @generated SignedSource<<7e09a7ad1d178850bdcf73da3eb5623b>>
|
|
*/
|
|
|
|
/**
|
|
* IMPORTANT: Do NOT modify this file directly.
|
|
*
|
|
* To change the definition of the flags, edit
|
|
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
|
|
*
|
|
* To regenerate this code, run the following script from the repo root:
|
|
* yarn featureflags-update
|
|
*/
|
|
|
|
#include "NativeReactNativeFeatureFlags.h"
|
|
#include <react/featureflags/ReactNativeFeatureFlags.h>
|
|
|
|
#ifdef RN_DISABLE_OSS_PLUGIN_HEADER
|
|
#include "Plugins.h"
|
|
#endif
|
|
|
|
std::shared_ptr<facebook::react::TurboModule>
|
|
NativeReactNativeFeatureFlagsModuleProvider(
|
|
std::shared_ptr<facebook::react::CallInvoker> jsInvoker) {
|
|
return std::make_shared<facebook::react::NativeReactNativeFeatureFlags>(
|
|
std::move(jsInvoker));
|
|
}
|
|
|
|
namespace facebook::react {
|
|
|
|
NativeReactNativeFeatureFlags::NativeReactNativeFeatureFlags(
|
|
std::shared_ptr<CallInvoker> jsInvoker)
|
|
: NativeReactNativeFeatureFlagsCxxSpec(std::move(jsInvoker)) {}
|
|
|
|
bool NativeReactNativeFeatureFlags::commonTestFlag(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::commonTestFlag();
|
|
}
|
|
|
|
bool NativeReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop();
|
|
}
|
|
|
|
bool NativeReactNativeFeatureFlags::enableBackgroundExecutor(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::enableBackgroundExecutor();
|
|
}
|
|
|
|
bool NativeReactNativeFeatureFlags::enableCustomDrawOrderFabric(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::enableCustomDrawOrderFabric();
|
|
}
|
|
|
|
bool NativeReactNativeFeatureFlags::enableFixForClippedSubviewsCrash(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::enableFixForClippedSubviewsCrash();
|
|
}
|
|
|
|
bool NativeReactNativeFeatureFlags::enableMicrotasks(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::enableMicrotasks();
|
|
}
|
|
|
|
bool NativeReactNativeFeatureFlags::enableMountHooksAndroid(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::enableMountHooksAndroid();
|
|
}
|
|
|
|
bool NativeReactNativeFeatureFlags::enableSpannableBuildingUnification(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::enableSpannableBuildingUnification();
|
|
}
|
|
|
|
bool NativeReactNativeFeatureFlags::enableUIConsistency(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::enableUIConsistency();
|
|
}
|
|
|
|
bool NativeReactNativeFeatureFlags::inspectorEnableCxxInspectorPackagerConnection(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::inspectorEnableCxxInspectorPackagerConnection();
|
|
}
|
|
|
|
bool NativeReactNativeFeatureFlags::inspectorEnableModernCDPRegistry(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::inspectorEnableModernCDPRegistry();
|
|
}
|
|
|
|
bool NativeReactNativeFeatureFlags::useModernRuntimeScheduler(
|
|
jsi::Runtime& /*runtime*/) {
|
|
return ReactNativeFeatureFlags::useModernRuntimeScheduler();
|
|
}
|
|
|
|
} // namespace facebook::react
|