mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Fabric: Shipping updateStateWithAutorepeat as the only way to update a state
Summary: This replaces the internal core implementation of `setState` with the new `updateStateWithAutorepeat` which is now the only option. In short, `updateStateWithAutorepeat` works as `setState` with the following features: * The state update might be performed several times until it succeeds. * The callback is being called on every retry with actual previous data provided (can be different on every call). * In case of a static value is provided (simple case, not lambda, the only case on Android for now), the same *new*/provided value will be used for all state updates. In this case, the state update cannot fail. * If a callback is provided, the update operation can be canceled via returning `nullptr` from the callback. This diff removes all mentions of the previous state update approach from the core; some other leftovers will be removed separatly. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D25695600 fbshipit-source-id: 14b3d4bad7ee69e024a9b0b9fc018f7d58bf060c
This commit is contained in:
committed by
Facebook GitHub Bot
parent
3ade096f02
commit
f379b1e583
@@ -61,7 +61,7 @@ using namespace facebook::react;
|
||||
auto newPadding = RCTEdgeInsetsFromUIEdgeInsets(insets);
|
||||
auto threshold = 1.0 / RCTScreenScale() + 0.01; // Size of a pixel plus some small threshold.
|
||||
|
||||
_state->updateStateWithAutorepeat(
|
||||
_state->updateState(
|
||||
[=](SafeAreaViewShadowNode::ConcreteState::Data const &oldData)
|
||||
-> SafeAreaViewShadowNode::ConcreteState::SharedData {
|
||||
auto oldPadding = oldData.padding;
|
||||
@@ -74,7 +74,7 @@ using namespace facebook::react;
|
||||
|
||||
auto newData = oldData;
|
||||
newData.padding = newPadding;
|
||||
return std::make_shared<SafeAreaViewShadowNode::ConcreteState::Data>(newData);
|
||||
return std::make_shared<SafeAreaViewShadowNode::ConcreteState::Data const>(newData);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -311,7 +311,7 @@ static void RCTSendPaperScrollEvent_DEPRECATED(UIScrollView *scrollView, NSInteg
|
||||
_state->updateState([contentOffset](ScrollViewShadowNode::ConcreteState::Data const &data) {
|
||||
auto newData = data;
|
||||
newData.contentOffset = contentOffset;
|
||||
return newData;
|
||||
return std::make_shared<ScrollViewShadowNode::ConcreteState::Data const>(newData);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,11 @@
|
||||
package com.facebook.react.fabric;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.facebook.jni.HybridData;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.bridge.NativeMap;
|
||||
import com.facebook.react.bridge.ReadableNativeMap;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.uimanager.StateWrapper;
|
||||
|
||||
@@ -30,9 +28,6 @@ public class StateWrapperImpl implements StateWrapper {
|
||||
|
||||
@DoNotStrip private final HybridData mHybridData;
|
||||
|
||||
private Runnable mFailureCallback = null;
|
||||
private int mUpdateStateId = 0;
|
||||
|
||||
private static native HybridData initHybrid();
|
||||
|
||||
private StateWrapperImpl() {
|
||||
@@ -44,29 +39,8 @@ public class StateWrapperImpl implements StateWrapper {
|
||||
|
||||
public native void updateStateImpl(@NonNull NativeMap map);
|
||||
|
||||
public native void updateStateWithFailureCallbackImpl(
|
||||
@NonNull NativeMap map, Object self, int updateStateId);
|
||||
|
||||
@Override
|
||||
public void updateState(@NonNull WritableMap map, Runnable failureCallback) {
|
||||
mUpdateStateId++;
|
||||
mFailureCallback = failureCallback;
|
||||
updateStateWithFailureCallbackImpl((NativeMap) map, this, mUpdateStateId);
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
@AnyThread
|
||||
public void updateStateFailed(int callbackRefId) {
|
||||
// If the callback ref ID doesn't match the ID of the most-recent updateState call,
|
||||
// then it's an outdated failure callback and we ignore it.
|
||||
if (callbackRefId != mUpdateStateId) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Runnable failureCallback = mFailureCallback;
|
||||
mFailureCallback = null;
|
||||
if (failureCallback != null) {
|
||||
UiThreadUtil.runOnUiThread(failureCallback);
|
||||
}
|
||||
public void updateState(@NonNull WritableMap map) {
|
||||
updateStateImpl((NativeMap) map);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,31 +33,7 @@ void StateWrapperImpl::updateStateImpl(NativeMap *map) {
|
||||
// Get folly::dynamic from map
|
||||
auto dynamicMap = map->consume();
|
||||
// Set state
|
||||
state_->updateState(dynamicMap, nullptr);
|
||||
}
|
||||
|
||||
void StateWrapperImpl::updateStateWithFailureCallbackImpl(
|
||||
NativeMap *map,
|
||||
jni::alias_ref<jobject> self,
|
||||
int callbackRefId) {
|
||||
// Get folly::dynamic from map
|
||||
auto dynamicMap = map->consume();
|
||||
// Turn the alias into a global_ref
|
||||
// Note: this whole thing feels really janky, making StateWrapperImpl.java
|
||||
// pass "this" into a function it's calling on "this". But after struggling
|
||||
// for a while I couldn't figure out how to get a reference to the Java side
|
||||
// of "this" in C++ in a way that's reasonably safe, and it maybe is even
|
||||
// discouraged. Anyway, it might be weird, but this seems to work and be safe.
|
||||
jni::global_ref<jobject> globalSelf = make_global(self);
|
||||
// Set state
|
||||
state_->updateState(
|
||||
dynamicMap, [globalSelf = std::move(globalSelf), callbackRefId]() {
|
||||
static auto method =
|
||||
jni::findClassStatic(
|
||||
StateWrapperImpl::StateWrapperImplJavaDescriptor)
|
||||
->getMethod<void(jint)>("updateStateFailed");
|
||||
method(globalSelf, callbackRefId);
|
||||
});
|
||||
state_->updateState(dynamicMap);
|
||||
}
|
||||
|
||||
void StateWrapperImpl::registerNatives() {
|
||||
@@ -65,9 +41,6 @@ void StateWrapperImpl::registerNatives() {
|
||||
makeNativeMethod("initHybrid", StateWrapperImpl::initHybrid),
|
||||
makeNativeMethod("getState", StateWrapperImpl::getState),
|
||||
makeNativeMethod("updateStateImpl", StateWrapperImpl::updateStateImpl),
|
||||
makeNativeMethod(
|
||||
"updateStateWithFailureCallbackImpl",
|
||||
StateWrapperImpl::updateStateWithFailureCallbackImpl),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -79,10 +79,9 @@ public class FabricViewStateManager {
|
||||
if (stateUpdate == null) {
|
||||
return;
|
||||
}
|
||||
stateWrapper.updateState(
|
||||
stateUpdate,
|
||||
// Failure callback - this is run if the updateState call fails
|
||||
failureRunnable);
|
||||
|
||||
// TODO: State update cannot fail; remove `failureRunnable` and custom retrying logic.
|
||||
stateWrapper.updateState(stateUpdate);
|
||||
}
|
||||
|
||||
public void setState(final StateUpdateCallback stateUpdateCallback) {
|
||||
|
||||
@@ -22,8 +22,8 @@ public interface StateWrapper {
|
||||
ReadableNativeMap getState();
|
||||
|
||||
/**
|
||||
* Pass a map of values back to the C++ layer. /Last/ runnable passed into updateState is called
|
||||
* if an updateState call fails.
|
||||
* Pass a map of values back to the C++ layer. The operation is performed synchronously and cannot
|
||||
* fail.
|
||||
*/
|
||||
void updateState(WritableMap map, Runnable failureCallback);
|
||||
void updateState(WritableMap map);
|
||||
}
|
||||
|
||||
@@ -60,55 +60,23 @@ class ConcreteState : public State {
|
||||
*/
|
||||
void updateState(
|
||||
Data &&newData,
|
||||
std::function<void()> failureCallback = nullptr,
|
||||
EventPriority priority = EventPriority::AsynchronousUnbatched) const {
|
||||
updateState(
|
||||
[data = std::move(newData)](Data const &oldData) mutable -> Data && {
|
||||
return std::move(data);
|
||||
[data = std::move(newData)](Data const &oldData) mutable -> SharedData {
|
||||
return std::make_shared<Data const>(std::move(data));
|
||||
},
|
||||
failureCallback,
|
||||
priority);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiate a state update process with a given function (that transforms an
|
||||
* old data value to a new one) and priority. The update function can be
|
||||
* called from any thread any moment later. The function can be called only
|
||||
* once or not called at all (in the case where the node was already unmounted
|
||||
* and updating makes no sense). The state update operation might fail in case
|
||||
* of conflict.
|
||||
* old data value to a new one) and priority. The callback function can be
|
||||
* called from any thread any moment later.
|
||||
* In case of a conflict, the `callback` might be called several times until
|
||||
* it succeeded. To cancel the state update operation, the callback needs to
|
||||
* return `nullptr`.
|
||||
*/
|
||||
void updateState(
|
||||
std::function<Data(Data const &oldData)> callback,
|
||||
std::function<void()> failureCallback = nullptr,
|
||||
EventPriority priority = EventPriority::AsynchronousBatched) const {
|
||||
auto family = family_.lock();
|
||||
|
||||
if (!family) {
|
||||
// No more nodes of this family exist anymore,
|
||||
// updating state is impossible.
|
||||
return;
|
||||
}
|
||||
|
||||
auto stateUpdate = StateUpdate{
|
||||
family,
|
||||
[=](StateData::Shared const &oldData) -> StateData::Shared {
|
||||
assert(oldData);
|
||||
return std::make_shared<Data const>(
|
||||
callback(*std::static_pointer_cast<Data const>(oldData)));
|
||||
},
|
||||
failureCallback,
|
||||
false};
|
||||
|
||||
family->dispatchRawState(std::move(stateUpdate), priority);
|
||||
}
|
||||
|
||||
/*
|
||||
* An experimental version of `updateState` function that re-commit the state
|
||||
* update over and over again until it succeeded. To cancel the state update
|
||||
* operation, the state update lambda needs to return `nullptr`.
|
||||
*/
|
||||
void updateStateWithAutorepeat(
|
||||
std::function<StateData::Shared(Data const &oldData)> callback,
|
||||
EventPriority priority = EventPriority::AsynchronousBatched) const {
|
||||
auto family = family_.lock();
|
||||
@@ -120,14 +88,10 @@ class ConcreteState : public State {
|
||||
}
|
||||
|
||||
auto stateUpdate = StateUpdate{
|
||||
family,
|
||||
[=](StateData::Shared const &oldData) -> StateData::Shared {
|
||||
family, [=](StateData::Shared const &oldData) -> StateData::Shared {
|
||||
assert(oldData);
|
||||
return callback(*std::static_pointer_cast<Data const>(oldData));
|
||||
},
|
||||
nullptr,
|
||||
true,
|
||||
};
|
||||
}};
|
||||
|
||||
family->dispatchRawState(std::move(stateUpdate), priority);
|
||||
}
|
||||
@@ -137,9 +101,8 @@ class ConcreteState : public State {
|
||||
return getData().getDynamic();
|
||||
}
|
||||
|
||||
void updateState(folly::dynamic data, std::function<void()> failureCallback)
|
||||
const override {
|
||||
updateState(std::move(Data(getData(), data)), failureCallback);
|
||||
void updateState(folly::dynamic data) const override {
|
||||
updateState(std::move(Data(getData(), data)));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -65,9 +65,7 @@ class State {
|
||||
|
||||
#ifdef ANDROID
|
||||
virtual folly::dynamic getDynamic() const = 0;
|
||||
virtual void updateState(
|
||||
folly::dynamic data,
|
||||
std::function<void()> failureCallback) const = 0;
|
||||
virtual void updateState(folly::dynamic data) const = 0;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
@@ -25,8 +25,6 @@ class StateUpdate {
|
||||
|
||||
SharedShadowNodeFamily family;
|
||||
Callback callback;
|
||||
FailureCallback failureCallback;
|
||||
bool autorepeat;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
|
||||
@@ -109,15 +109,9 @@ Scheduler::Scheduler(
|
||||
#ifdef ANDROID
|
||||
removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool(
|
||||
"react_fabric:remove_outstanding_surfaces_on_destruction_android");
|
||||
uiManager_->experimentEnableStateUpdateWithAutorepeat =
|
||||
reactNativeConfig_->getBool(
|
||||
"react_fabric:enable_state_update_with_autorepeat_android");
|
||||
#else
|
||||
removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool(
|
||||
"react_fabric:remove_outstanding_surfaces_on_destruction_ios");
|
||||
uiManager_->experimentEnableStateUpdateWithAutorepeat =
|
||||
reactNativeConfig_->getBool(
|
||||
"react_fabric:enable_state_update_with_autorepeat_ios");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -191,8 +191,7 @@ LayoutMetrics UIManager::getRelativeLayoutMetrics(
|
||||
shadowNode.getFamily(), *layoutableAncestorShadowNode, policy);
|
||||
}
|
||||
|
||||
void UIManager::updateStateWithAutorepeat(
|
||||
StateUpdate const &stateUpdate) const {
|
||||
void UIManager::updateState(StateUpdate const &stateUpdate) const {
|
||||
auto &callback = stateUpdate.callback;
|
||||
auto &family = stateUpdate.family;
|
||||
auto &componentDescriptor = family->getComponentDescriptor();
|
||||
@@ -230,43 +229,6 @@ void UIManager::updateStateWithAutorepeat(
|
||||
});
|
||||
}
|
||||
|
||||
void UIManager::updateState(StateUpdate const &stateUpdate) const {
|
||||
if (stateUpdate.autorepeat || experimentEnableStateUpdateWithAutorepeat) {
|
||||
updateStateWithAutorepeat(stateUpdate);
|
||||
return;
|
||||
}
|
||||
|
||||
auto &callback = stateUpdate.callback;
|
||||
auto &family = stateUpdate.family;
|
||||
auto &componentDescriptor = family->getComponentDescriptor();
|
||||
|
||||
shadowTreeRegistry_.visit(
|
||||
family->getSurfaceId(), [&](ShadowTree const &shadowTree) {
|
||||
auto status = shadowTree.tryCommit([&](RootShadowNode const
|
||||
&oldRootShadowNode) {
|
||||
return std::static_pointer_cast<RootShadowNode>(
|
||||
oldRootShadowNode.cloneTree(
|
||||
*family, [&](ShadowNode const &oldShadowNode) {
|
||||
auto newData =
|
||||
callback(oldShadowNode.getState()->getDataPointer());
|
||||
auto newState =
|
||||
componentDescriptor.createState(*family, newData);
|
||||
|
||||
return oldShadowNode.clone({
|
||||
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
|
||||
/* .children = */
|
||||
ShadowNodeFragment::childrenPlaceholder(),
|
||||
/* .state = */ newState,
|
||||
});
|
||||
}));
|
||||
});
|
||||
if (status != ShadowTree::CommitStatus::Succeeded &&
|
||||
stateUpdate.failureCallback) {
|
||||
stateUpdate.failureCallback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void UIManager::dispatchCommand(
|
||||
const ShadowNode::Shared &shadowNode,
|
||||
std::string const &commandName,
|
||||
|
||||
@@ -83,11 +83,6 @@ class UIManager final : public ShadowTreeDelegate {
|
||||
ShadowTree const &shadowTree,
|
||||
MountingCoordinator::Shared const &mountingCoordinator) const override;
|
||||
|
||||
/*
|
||||
* Temporary flags.
|
||||
*/
|
||||
bool experimentEnableStateUpdateWithAutorepeat{false};
|
||||
|
||||
RootShadowNode::Unshared shadowTreeWillCommit(
|
||||
ShadowTree const &shadowTree,
|
||||
RootShadowNode::Shared const &oldRootShadowNode,
|
||||
@@ -143,7 +138,6 @@ class UIManager final : public ShadowTreeDelegate {
|
||||
* and performs a commit.
|
||||
*/
|
||||
void updateState(StateUpdate const &stateUpdate) const;
|
||||
void updateStateWithAutorepeat(StateUpdate const &stateUpdate) const;
|
||||
|
||||
void dispatchCommand(
|
||||
const ShadowNode::Shared &shadowNode,
|
||||
|
||||
Reference in New Issue
Block a user