@onekeyfe/react-native-skeleton 3.0.96 → 3.0.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. package/android/src/main/java/com/margelo/nitro/skeleton/OneKeySkeletonRenderer.kt +173 -0
  2. package/android/src/main/java/com/margelo/nitro/skeleton/Skeleton.kt +63 -194
  3. package/ios/Skeleton.swift +58 -272
  4. package/ios/SkeletonRenderer.swift +146 -0
  5. package/lib/nitrogen/generated/android/c++/views/JHybridSkeletonStateUpdater.cpp +42 -23
  6. package/lib/nitrogen/generated/android/c++/views/JHybridSkeletonStateUpdater.hpp +6 -1
  7. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/skeleton/views/HybridSkeletonManager.kt +26 -10
  8. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/skeleton/views/HybridSkeletonStateUpdater.kt +2 -2
  9. package/lib/nitrogen/generated/ios/c++/views/HybridSkeletonComponent.mm +50 -27
  10. package/lib/nitrogen/generated/shared/c++/views/HybridSkeletonComponent.cpp +7 -65
  11. package/lib/nitrogen/generated/shared/c++/views/HybridSkeletonComponent.hpp +26 -50
  12. package/nitrogen/generated/android/c++/views/JHybridSkeletonStateUpdater.cpp +42 -23
  13. package/nitrogen/generated/android/c++/views/JHybridSkeletonStateUpdater.hpp +6 -1
  14. package/nitrogen/generated/android/kotlin/com/margelo/nitro/skeleton/views/HybridSkeletonManager.kt +26 -10
  15. package/nitrogen/generated/android/kotlin/com/margelo/nitro/skeleton/views/HybridSkeletonStateUpdater.kt +2 -2
  16. package/nitrogen/generated/ios/c++/views/HybridSkeletonComponent.mm +50 -27
  17. package/nitrogen/generated/shared/c++/views/HybridSkeletonComponent.cpp +7 -65
  18. package/nitrogen/generated/shared/c++/views/HybridSkeletonComponent.hpp +26 -50
  19. package/package.json +4 -4
@@ -20,6 +20,14 @@ import com.margelo.nitro.skeleton.*
20
20
  * Represents the React Native `ViewManager` for the "Skeleton" Nitro HybridView.
21
21
  */
22
22
  public class HybridSkeletonManager: SimpleViewManager<View>() {
23
+ /**
24
+ * Represents the View and its last state snapshot (mutable)
25
+ */
26
+ private class HybridViewHolder(
27
+ val hybridView: HybridSkeleton,
28
+ var lastState: StateWrapper? = null,
29
+ )
30
+
23
31
  init {
24
32
  if (RecyclableView::class.java.isAssignableFrom(HybridSkeleton::class.java)) {
25
33
  // Enable view recycling
@@ -34,33 +42,41 @@ public class HybridSkeletonManager: SimpleViewManager<View>() {
34
42
  override fun createViewInstance(reactContext: ThemedReactContext): View {
35
43
  val hybridView = HybridSkeleton(reactContext)
36
44
  val view = hybridView.view
37
- view.setTag(associated_hybrid_view_tag, hybridView)
45
+ view.setTag(associated_hybrid_view_tag, HybridViewHolder(hybridView))
38
46
  return view
39
47
  }
40
48
 
41
49
  override fun updateState(view: View, props: ReactStylesDiffMap, stateWrapper: StateWrapper): Any? {
42
- val hybridView = getHybridView(view)
50
+ val holder = getHybridViewHolder(view)
43
51
  ?: throw Error("Couldn't find view $view in local views table!")
52
+ val hybridView = holder.hybridView
53
+ val oldState = holder.lastState
54
+ val newState = stateWrapper
44
55
 
45
56
  // 1. Update each prop individually
46
57
  hybridView.beforeUpdate()
47
- HybridSkeletonStateUpdater.updateViewProps(hybridView, stateWrapper)
58
+ HybridSkeletonStateUpdater.updateViewProps(hybridView, newState, oldState)
48
59
  hybridView.afterUpdate()
60
+ holder.lastState = newState
49
61
 
50
62
  // 2. Continue in base View props
51
- return super.updateState(view, props, stateWrapper)
63
+ return super.updateState(view, props, newState)
52
64
  }
53
65
 
54
66
  override fun onDropViewInstance(view: View) {
55
- val hybridView = getHybridView(view)
56
- hybridView?.onDropView()
67
+ val holder = getHybridViewHolder(view)
68
+ holder?.lastState = null
69
+ holder?.hybridView?.onDropView()
57
70
  return super.onDropViewInstance(view)
58
71
  }
59
72
 
60
73
  protected override fun prepareToRecycleView(reactContext: ThemedReactContext, view: View): View? {
61
- super.prepareToRecycleView(reactContext, view)
62
- val hybridView = getHybridView(view)
74
+ val preparedView = super.prepareToRecycleView(reactContext, view)
75
+ ?: return null
76
+ val holder = getHybridViewHolder(preparedView)
63
77
  ?: return null
78
+ val hybridView = holder.hybridView
79
+ holder.lastState = null
64
80
 
65
81
  @Suppress("USELESS_IS_CHECK")
66
82
  if (hybridView is RecyclableView) {
@@ -74,7 +90,7 @@ public class HybridSkeletonManager: SimpleViewManager<View>() {
74
90
  }
75
91
  }
76
92
 
77
- private fun getHybridView(view: View): HybridSkeleton? {
78
- return view.getTag(associated_hybrid_view_tag) as? HybridSkeleton
93
+ private fun getHybridViewHolder(view: View): HybridViewHolder? {
94
+ return view.getTag(associated_hybrid_view_tag) as? HybridViewHolder
79
95
  }
80
96
  }
@@ -14,10 +14,10 @@ internal class HybridSkeletonStateUpdater {
14
14
  companion object {
15
15
  /**
16
16
  * Updates the props for [view] through C++.
17
- * The [state] prop is expected to contain [view]'s props as wrapped Fabric state.
17
+ * The [newState] prop is expected to contain [view]'s props as wrapped Fabric state.
18
18
  */
19
19
  @Suppress("KotlinJniMissingFunction")
20
20
  @JvmStatic
21
- external fun updateViewProps(view: HybridSkeletonSpec, state: StateWrapper)
21
+ external fun updateViewProps(view: HybridSkeletonSpec, newState: StateWrapper, oldState: StateWrapper?)
22
22
  }
23
23
  }
@@ -37,6 +37,7 @@ using namespace margelo::nitro::skeleton::views;
37
37
 
38
38
  @implementation HybridSkeletonComponent {
39
39
  std::shared_ptr<HybridSkeletonSpecSwift> _hybridView;
40
+ BOOL _didDropView;
40
41
  }
41
42
 
42
43
  + (void) load {
@@ -50,6 +51,7 @@ using namespace margelo::nitro::skeleton::views;
50
51
 
51
52
  - (instancetype) init {
52
53
  if (self = [super init]) {
54
+ _props = HybridSkeletonShadowNode::defaultSharedProps();
53
55
  std::shared_ptr<HybridSkeletonSpec> hybridView = Skeleton::SkeletonAutolinking::createSkeleton();
54
56
  _hybridView = std::dynamic_pointer_cast<HybridSkeletonSpecSwift>(hybridView);
55
57
  [self updateView];
@@ -69,40 +71,61 @@ using namespace margelo::nitro::skeleton::views;
69
71
  [self setContentView:view];
70
72
  }
71
73
 
74
+ - (void) notifyOnDropView {
75
+ // A recycled component can later be invalidated. Notify only once per mount.
76
+ if (_didDropView) {
77
+ return;
78
+ }
79
+ Skeleton::HybridSkeletonSpec_cxx& swiftPart = _hybridView->getSwiftPart();
80
+ swiftPart.onDropView();
81
+ _didDropView = YES;
82
+ }
83
+
72
84
  - (void) updateProps:(const std::shared_ptr<const react::Props>&)props
73
85
  oldProps:(const std::shared_ptr<const react::Props>&)oldProps {
86
+ // A props update marks a newly mounted or still-active component.
87
+ _didDropView = NO;
88
+
74
89
  // 1. Downcast props
75
- const auto& newViewPropsConst = *std::static_pointer_cast<HybridSkeletonProps const>(props);
76
- auto& newViewProps = const_cast<HybridSkeletonProps&>(newViewPropsConst);
90
+ const auto& newViewProps = *std::static_pointer_cast<const HybridSkeletonProps>(props);
91
+ const auto* oldViewProps = static_cast<const HybridSkeletonProps*>(oldProps.get());
77
92
  Skeleton::HybridSkeletonSpec_cxx& swiftPart = _hybridView->getSwiftPart();
78
93
 
79
- // 2. Update each prop individually
80
- swiftPart.beforeUpdate();
81
-
82
- // shimmerSpeed: optional
83
- if (newViewProps.shimmerSpeed.isDirty) {
84
- swiftPart.setShimmerSpeed(newViewProps.shimmerSpeed.value);
85
- newViewProps.shimmerSpeed.isDirty = false;
86
- }
87
- // shimmerGradientColors: optional
88
- if (newViewProps.shimmerGradientColors.isDirty) {
89
- swiftPart.setShimmerGradientColors(newViewProps.shimmerGradientColors.value);
90
- newViewProps.shimmerGradientColors.isDirty = false;
91
- }
92
-
93
- swiftPart.afterUpdate();
94
+ // 2. Update only props that differ from the previous Props snapshot.
95
+ const bool hasTransactionPropChanges = oldViewProps == nullptr
96
+ ? newViewProps.hasAnyProvidedProps()
97
+ : !newViewProps.hasSameProps(*oldViewProps);
98
+ if (hasTransactionPropChanges) {
99
+ swiftPart.beforeUpdate();
100
+
101
+ // shimmerSpeed: optional
102
+ if (oldViewProps == nullptr
103
+ ? newViewProps.shimmerSpeed.isProvided()
104
+ : !newViewProps.shimmerSpeed.hasSameValue(oldViewProps->shimmerSpeed)) {
105
+ swiftPart.setShimmerSpeed(newViewProps.shimmerSpeed.get());
106
+ }
107
+ // shimmerGradientColors: optional
108
+ if (oldViewProps == nullptr
109
+ ? newViewProps.shimmerGradientColors.isProvided()
110
+ : !newViewProps.shimmerGradientColors.hasSameValue(oldViewProps->shimmerGradientColors)) {
111
+ swiftPart.setShimmerGradientColors(newViewProps.shimmerGradientColors.get());
112
+ }
94
113
 
95
- // 3. Update hybridRef if it changed
96
- if (newViewProps.hybridRef.isDirty) {
97
- // hybridRef changed - call it with new this
98
- const auto& maybeFunc = newViewProps.hybridRef.value;
99
- if (maybeFunc.has_value()) {
100
- maybeFunc.value()(_hybridView);
114
+ // Update hybridRef if it changed
115
+ if (oldViewProps == nullptr
116
+ ? newViewProps.hybridRef.isProvided()
117
+ : !newViewProps.hybridRef.hasSameValue(oldViewProps->hybridRef)) {
118
+ // hybridRef changed - call it with new this
119
+ const auto& maybeFunc = newViewProps.hybridRef.get();
120
+ if (maybeFunc.has_value()) {
121
+ maybeFunc.value()(_hybridView);
122
+ }
101
123
  }
102
- newViewProps.hybridRef.isDirty = false;
124
+
125
+ swiftPart.afterUpdate();
103
126
  }
104
127
 
105
- // 4. Continue in base class
128
+ // 3. Continue in base class
106
129
  [super updateProps:props oldProps:oldProps];
107
130
  }
108
131
 
@@ -111,6 +134,7 @@ using namespace margelo::nitro::skeleton::views;
111
134
  }
112
135
 
113
136
  - (void)prepareForRecycle {
137
+ [self notifyOnDropView];
114
138
  [super prepareForRecycle];
115
139
  Skeleton::HybridSkeletonSpec_cxx& swiftPart = _hybridView->getSwiftPart();
116
140
  swiftPart.maybePrepareForRecycle();
@@ -118,8 +142,7 @@ using namespace margelo::nitro::skeleton::views;
118
142
 
119
143
  #ifdef ENABLE_RCT_COMPONENT_VIEW_INVALIDATE
120
144
  - (void)invalidate {
121
- Skeleton::HybridSkeletonSpec_cxx& swiftPart = _hybridView->getSwiftPart();
122
- swiftPart.onDropView();
145
+ [self notifyOnDropView];
123
146
  [super invalidate];
124
147
  }
125
148
  #endif
@@ -7,55 +7,22 @@
7
7
 
8
8
  #include "HybridSkeletonComponent.hpp"
9
9
 
10
- #include <string>
11
- #include <exception>
12
- #include <utility>
13
- #include <NitroModules/NitroDefines.hpp>
14
- #include <NitroModules/JSIConverter.hpp>
15
- #include <NitroModules/PropNameIDCache.hpp>
16
- #include <react/renderer/core/RawValue.h>
17
- #include <react/renderer/core/ShadowNode.h>
18
- #include <react/renderer/core/ComponentDescriptor.h>
19
- #include <react/renderer/components/view/ViewProps.h>
10
+ #include <NitroModules/NitroHash.hpp>
11
+ #include <NitroModules/ReactProp.hpp>
20
12
 
21
13
  namespace margelo::nitro::skeleton::views {
22
14
 
15
+ using namespace facebook;
16
+
23
17
  extern const char HybridSkeletonComponentName[] = "Skeleton";
24
18
 
25
19
  HybridSkeletonProps::HybridSkeletonProps(const react::PropsParserContext& context,
26
20
  const HybridSkeletonProps& sourceProps,
27
21
  const react::RawProps& rawProps):
28
22
  react::ViewProps(context, sourceProps, rawProps, filterObjectKeys),
29
- shimmerSpeed([&]() -> CachedProp<std::optional<double>> {
30
- try {
31
- const react::RawValue* rawValue = rawProps.at("shimmerSpeed", nullptr, nullptr);
32
- if (rawValue == nullptr) return sourceProps.shimmerSpeed;
33
- const auto& [runtime, value] = (std::pair<jsi::Runtime*, jsi::Value>)*rawValue;
34
- return CachedProp<std::optional<double>>::fromRawValue(*runtime, value, sourceProps.shimmerSpeed);
35
- } catch (const std::exception& exc) {
36
- throw std::runtime_error(std::string("Skeleton.shimmerSpeed: ") + exc.what());
37
- }
38
- }()),
39
- shimmerGradientColors([&]() -> CachedProp<std::optional<std::vector<std::string>>> {
40
- try {
41
- const react::RawValue* rawValue = rawProps.at("shimmerGradientColors", nullptr, nullptr);
42
- if (rawValue == nullptr) return sourceProps.shimmerGradientColors;
43
- const auto& [runtime, value] = (std::pair<jsi::Runtime*, jsi::Value>)*rawValue;
44
- return CachedProp<std::optional<std::vector<std::string>>>::fromRawValue(*runtime, value, sourceProps.shimmerGradientColors);
45
- } catch (const std::exception& exc) {
46
- throw std::runtime_error(std::string("Skeleton.shimmerGradientColors: ") + exc.what());
47
- }
48
- }()),
49
- hybridRef([&]() -> CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridSkeletonSpec>& /* ref */)>>> {
50
- try {
51
- const react::RawValue* rawValue = rawProps.at("hybridRef", nullptr, nullptr);
52
- if (rawValue == nullptr) return sourceProps.hybridRef;
53
- const auto& [runtime, value] = (std::pair<jsi::Runtime*, jsi::Value>)*rawValue;
54
- return CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridSkeletonSpec>& /* ref */)>>>::fromRawValue(*runtime, value.asObject(*runtime).getProperty(*runtime, PropNameIDCache::get(*runtime, "f")), sourceProps.hybridRef);
55
- } catch (const std::exception& exc) {
56
- throw std::runtime_error(std::string("Skeleton.hybridRef: ") + exc.what());
57
- }
58
- }()) { }
23
+ shimmerSpeed(nitro::ReactProp<std::optional<double>>::fromRawValue("Skeleton", "shimmerSpeed", rawProps, sourceProps.shimmerSpeed)),
24
+ shimmerGradientColors(nitro::ReactProp<std::optional<std::vector<std::string>>>::fromRawValue("Skeleton", "shimmerGradientColors", rawProps, sourceProps.shimmerGradientColors)),
25
+ hybridRef(nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridSkeletonSpec>& /* ref */)>>>::fromRawValue("Skeleton", "hybridRef", rawProps, sourceProps.hybridRef)) { }
59
26
 
60
27
  bool HybridSkeletonProps::filterObjectKeys(const std::string& propName) {
61
28
  switch (hashString(propName)) {
@@ -66,29 +33,4 @@ namespace margelo::nitro::skeleton::views {
66
33
  }
67
34
  }
68
35
 
69
- HybridSkeletonComponentDescriptor::HybridSkeletonComponentDescriptor(const react::ComponentDescriptorParameters& parameters)
70
- : ConcreteComponentDescriptor(parameters,
71
- react::RawPropsParser()) {}
72
-
73
- std::shared_ptr<const react::Props> HybridSkeletonComponentDescriptor::cloneProps(const react::PropsParserContext& context,
74
- const std::shared_ptr<const react::Props>& props,
75
- react::RawProps rawProps) const {
76
- // 1. Prepare raw props parser
77
- rawProps.parse(rawPropsParser_);
78
- // 2. Copy props with Nitro's cached copy constructor
79
- return HybridSkeletonShadowNode::Props(context, /* & */ rawProps, props);
80
- }
81
-
82
- #ifdef ANDROID
83
- void HybridSkeletonComponentDescriptor::adopt(react::ShadowNode& shadowNode) const {
84
- // This is called immediately after `ShadowNode` is created, cloned or in progress.
85
- // On Android, we need to wrap props in our state, which gets routed through Java and later unwrapped in JNI/C++.
86
- auto& concreteShadowNode = static_cast<HybridSkeletonShadowNode&>(shadowNode);
87
- const std::shared_ptr<const HybridSkeletonProps>& constProps = concreteShadowNode.getConcreteSharedProps();
88
- const std::shared_ptr<HybridSkeletonProps>& props = std::const_pointer_cast<HybridSkeletonProps>(constProps);
89
- HybridSkeletonState state{props};
90
- concreteShadowNode.setStateData(std::move(state));
91
- }
92
- #endif
93
-
94
36
  } // namespace margelo::nitro::skeleton::views
@@ -7,14 +7,15 @@
7
7
 
8
8
  #pragma once
9
9
 
10
- #include <optional>
11
- #include <NitroModules/NitroDefines.hpp>
12
- #include <NitroModules/NitroHash.hpp>
13
- #include <NitroModules/CachedProp.hpp>
14
- #include <react/renderer/core/ConcreteComponentDescriptor.h>
15
- #include <react/renderer/core/PropsParserContext.h>
10
+ #include <NitroModules/ReactProp.hpp>
11
+ #include <NitroModules/ViewComponentDescriptor.hpp>
12
+ #include <NitroModules/ViewPropsHolderState.hpp>
16
13
  #include <react/renderer/components/view/ConcreteViewShadowNode.h>
17
14
  #include <react/renderer/components/view/ViewProps.h>
15
+ #include <react/renderer/core/PropsParserContext.h>
16
+ #include <react/renderer/core/RawProps.h>
17
+
18
+ #include <string>
18
19
 
19
20
  #include <optional>
20
21
  #include <string>
@@ -43,9 +44,23 @@ namespace margelo::nitro::skeleton::views {
43
44
  const react::RawProps& rawProps);
44
45
 
45
46
  public:
46
- CachedProp<std::optional<double>> shimmerSpeed;
47
- CachedProp<std::optional<std::vector<std::string>>> shimmerGradientColors;
48
- CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridSkeletonSpec>& /* ref */)>>> hybridRef;
47
+ nitro::ReactProp<std::optional<double>> shimmerSpeed;
48
+ nitro::ReactProp<std::optional<std::vector<std::string>>> shimmerGradientColors;
49
+ nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridSkeletonSpec>& /* ref */)>>> hybridRef;
50
+
51
+ [[nodiscard]]
52
+ bool hasSameProps(const HybridSkeletonProps& other) const noexcept {
53
+ return shimmerSpeed.hasSameValue(other.shimmerSpeed) &&
54
+ shimmerGradientColors.hasSameValue(other.shimmerGradientColors) &&
55
+ hybridRef.hasSameValue(other.hybridRef);
56
+ }
57
+
58
+ [[nodiscard]]
59
+ bool hasAnyProvidedProps() const noexcept {
60
+ return shimmerSpeed.isProvided() ||
61
+ shimmerGradientColors.isProvided() ||
62
+ hybridRef.isProvided();
63
+ }
49
64
 
50
65
  private:
51
66
  static bool filterObjectKeys(const std::string& propName);
@@ -54,32 +69,7 @@ namespace margelo::nitro::skeleton::views {
54
69
  /**
55
70
  * State for the "Skeleton" View.
56
71
  */
57
- class HybridSkeletonState final {
58
- public:
59
- HybridSkeletonState() = default;
60
- explicit HybridSkeletonState(const std::shared_ptr<HybridSkeletonProps>& props):
61
- _props(props) {}
62
-
63
- public:
64
- [[nodiscard]]
65
- const std::shared_ptr<HybridSkeletonProps>& getProps() const {
66
- return _props;
67
- }
68
-
69
- public:
70
- #ifdef ANDROID
71
- HybridSkeletonState(const HybridSkeletonState& /* previousState */, folly::dynamic /* data */) {}
72
- folly::dynamic getDynamic() const {
73
- throw std::runtime_error("HybridSkeletonState does not support folly!");
74
- }
75
- react::MapBuffer getMapBuffer() const {
76
- throw std::runtime_error("HybridSkeletonState does not support MapBuffer!");
77
- };
78
- #endif
79
-
80
- private:
81
- std::shared_ptr<HybridSkeletonProps> _props;
82
- };
72
+ using HybridSkeletonState = nitro::ViewPropsHolderState<HybridSkeletonProps>;
83
73
 
84
74
  /**
85
75
  * The Shadow Node for the "Skeleton" View.
@@ -92,21 +82,7 @@ namespace margelo::nitro::skeleton::views {
92
82
  /**
93
83
  * The Component Descriptor for the "Skeleton" View.
94
84
  */
95
- class HybridSkeletonComponentDescriptor final: public react::ConcreteComponentDescriptor<HybridSkeletonShadowNode> {
96
- public:
97
- explicit HybridSkeletonComponentDescriptor(const react::ComponentDescriptorParameters& parameters);
98
-
99
- public:
100
- /**
101
- * A faster path for cloning props - reuses the caching logic from `HybridSkeletonProps`.
102
- */
103
- std::shared_ptr<const react::Props> cloneProps(const react::PropsParserContext& context,
104
- const std::shared_ptr<const react::Props>& props,
105
- react::RawProps rawProps) const override;
106
- #ifdef ANDROID
107
- void adopt(react::ShadowNode& shadowNode) const override;
108
- #endif
109
- };
85
+ using HybridSkeletonComponentDescriptor = nitro::ViewComponentDescriptor<HybridSkeletonShadowNode>;
110
86
 
111
87
  /* The actual view for "Skeleton" needs to be implemented in platform-specific code. */
112
88
 
@@ -15,45 +15,64 @@ namespace margelo::nitro::skeleton::views {
15
15
  using namespace facebook;
16
16
  using ConcreteStateData = react::ConcreteState<HybridSkeletonState>;
17
17
 
18
- void JHybridSkeletonStateUpdater::updateViewProps(jni::alias_ref<jni::JClass> /* class */,
19
- jni::alias_ref<JHybridSkeletonSpec::JavaPart> javaView,
20
- jni::alias_ref<JStateWrapper::javaobject> stateWrapperInterface) {
21
- std::shared_ptr<JHybridSkeletonSpec> hybridView = javaView->getJHybridSkeletonSpec();
22
-
18
+ std::shared_ptr<const HybridSkeletonProps> JHybridSkeletonStateUpdater::getPropsFromStateWrapper(
19
+ jni::alias_ref<JStateWrapper::javaobject> stateWrapper) {
20
+ if (stateWrapper.get() == nullptr) {
21
+ return nullptr;
22
+ }
23
23
  // Get concrete StateWrapperImpl from passed StateWrapper interface object
24
- jobject rawStateWrapper = stateWrapperInterface.get();
25
- if (!stateWrapperInterface->isInstanceOf(react::StateWrapperImpl::javaClassStatic())) [[unlikely]] {
26
- throw std::runtime_error("StateWrapper is not a StateWrapperImpl");
24
+ jobject rawStateWrapper = stateWrapper.get();
25
+ if (!stateWrapper->isInstanceOf(react::StateWrapperImpl::javaClassStatic())) [[unlikely]] {
26
+ throw std::runtime_error("StateWrapper is not a StateWrapperImpl");
27
+ }
28
+ auto stateWrapperImpl = jni::alias_ref<react::StateWrapperImpl::javaobject>{
29
+ static_cast<react::StateWrapperImpl::javaobject>(rawStateWrapper)
30
+ };
31
+ std::shared_ptr<const react::State> state = stateWrapperImpl->cthis()->getState();
32
+ if (state == nullptr) {
33
+ return nullptr;
27
34
  }
28
- auto stateWrapper = jni::alias_ref<react::StateWrapperImpl::javaobject>{
29
- static_cast<react::StateWrapperImpl::javaobject>(rawStateWrapper)};
30
- std::shared_ptr<const react::State> state = stateWrapper->cthis()->getState();
31
35
  auto concreteState = std::static_pointer_cast<const ConcreteStateData>(state);
32
36
  const HybridSkeletonState& data = concreteState->getData();
33
- const std::shared_ptr<HybridSkeletonProps>& props = data.getProps();
37
+ const std::shared_ptr<const HybridSkeletonProps>& props = data.getProps();
34
38
  if (props == nullptr) [[unlikely]] {
35
- // Props aren't set yet!
36
39
  throw std::runtime_error("HybridSkeletonState's data doesn't contain any props!");
37
40
  }
41
+ return props;
42
+ }
43
+
44
+ void JHybridSkeletonStateUpdater::updateViewProps(jni::alias_ref<jni::JClass> /* class */,
45
+ jni::alias_ref<JHybridSkeletonSpec::JavaPart> javaView,
46
+ jni::alias_ref<JStateWrapper::javaobject> newState,
47
+ jni::alias_ref<JStateWrapper::javaobject> oldState) {
48
+ std::shared_ptr<JHybridSkeletonSpec> hybridView = javaView->getJHybridSkeletonSpec();
49
+ std::shared_ptr<const HybridSkeletonProps> newProps = getPropsFromStateWrapper(newState);
50
+ std::shared_ptr<const HybridSkeletonProps> oldProps = getPropsFromStateWrapper(oldState);
51
+ if (newProps == nullptr) [[unlikely]] {
52
+ throw std::runtime_error("Current StateWrapper doesn't contain any props!");
53
+ }
38
54
 
39
- // Update all props if they are dirty
40
- if (props->shimmerSpeed.isDirty) {
41
- hybridView->setShimmerSpeed(props->shimmerSpeed.value);
42
- props->shimmerSpeed.isDirty = false;
55
+ // Update only props that differ from the previous State snapshot.
56
+ if (oldProps == nullptr
57
+ ? newProps->shimmerSpeed.isProvided()
58
+ : !newProps->shimmerSpeed.hasSameValue(oldProps->shimmerSpeed)) {
59
+ hybridView->setShimmerSpeed(newProps->shimmerSpeed.get());
43
60
  }
44
- if (props->shimmerGradientColors.isDirty) {
45
- hybridView->setShimmerGradientColors(props->shimmerGradientColors.value);
46
- props->shimmerGradientColors.isDirty = false;
61
+ if (oldProps == nullptr
62
+ ? newProps->shimmerGradientColors.isProvided()
63
+ : !newProps->shimmerGradientColors.hasSameValue(oldProps->shimmerGradientColors)) {
64
+ hybridView->setShimmerGradientColors(newProps->shimmerGradientColors.get());
47
65
  }
48
66
 
49
67
  // Update hybridRef if it changed
50
- if (props->hybridRef.isDirty) {
68
+ if (oldProps == nullptr
69
+ ? newProps->hybridRef.isProvided()
70
+ : !newProps->hybridRef.hasSameValue(oldProps->hybridRef)) {
51
71
  // hybridRef changed - call it with new this
52
- const auto& maybeFunc = props->hybridRef.value;
72
+ const auto& maybeFunc = newProps->hybridRef.get();
53
73
  if (maybeFunc.has_value()) {
54
74
  maybeFunc.value()(hybridView);
55
75
  }
56
- props->hybridRef.isDirty = false;
57
76
  }
58
77
  }
59
78
 
@@ -31,7 +31,12 @@ public:
31
31
  public:
32
32
  static void updateViewProps(jni::alias_ref<jni::JClass> /* class */,
33
33
  jni::alias_ref<JHybridSkeletonSpec::JavaPart> view,
34
- jni::alias_ref<JStateWrapper::javaobject> stateWrapperInterface);
34
+ jni::alias_ref<JStateWrapper::javaobject> newState,
35
+ jni::alias_ref<JStateWrapper::javaobject> oldState);
36
+
37
+ private:
38
+ static std::shared_ptr<const HybridSkeletonProps> getPropsFromStateWrapper(
39
+ jni::alias_ref<JStateWrapper::javaobject> stateWrapper);
35
40
 
36
41
  public:
37
42
  static void registerNatives() {
@@ -20,6 +20,14 @@ import com.margelo.nitro.skeleton.*
20
20
  * Represents the React Native `ViewManager` for the "Skeleton" Nitro HybridView.
21
21
  */
22
22
  public class HybridSkeletonManager: SimpleViewManager<View>() {
23
+ /**
24
+ * Represents the View and its last state snapshot (mutable)
25
+ */
26
+ private class HybridViewHolder(
27
+ val hybridView: HybridSkeleton,
28
+ var lastState: StateWrapper? = null,
29
+ )
30
+
23
31
  init {
24
32
  if (RecyclableView::class.java.isAssignableFrom(HybridSkeleton::class.java)) {
25
33
  // Enable view recycling
@@ -34,33 +42,41 @@ public class HybridSkeletonManager: SimpleViewManager<View>() {
34
42
  override fun createViewInstance(reactContext: ThemedReactContext): View {
35
43
  val hybridView = HybridSkeleton(reactContext)
36
44
  val view = hybridView.view
37
- view.setTag(associated_hybrid_view_tag, hybridView)
45
+ view.setTag(associated_hybrid_view_tag, HybridViewHolder(hybridView))
38
46
  return view
39
47
  }
40
48
 
41
49
  override fun updateState(view: View, props: ReactStylesDiffMap, stateWrapper: StateWrapper): Any? {
42
- val hybridView = getHybridView(view)
50
+ val holder = getHybridViewHolder(view)
43
51
  ?: throw Error("Couldn't find view $view in local views table!")
52
+ val hybridView = holder.hybridView
53
+ val oldState = holder.lastState
54
+ val newState = stateWrapper
44
55
 
45
56
  // 1. Update each prop individually
46
57
  hybridView.beforeUpdate()
47
- HybridSkeletonStateUpdater.updateViewProps(hybridView, stateWrapper)
58
+ HybridSkeletonStateUpdater.updateViewProps(hybridView, newState, oldState)
48
59
  hybridView.afterUpdate()
60
+ holder.lastState = newState
49
61
 
50
62
  // 2. Continue in base View props
51
- return super.updateState(view, props, stateWrapper)
63
+ return super.updateState(view, props, newState)
52
64
  }
53
65
 
54
66
  override fun onDropViewInstance(view: View) {
55
- val hybridView = getHybridView(view)
56
- hybridView?.onDropView()
67
+ val holder = getHybridViewHolder(view)
68
+ holder?.lastState = null
69
+ holder?.hybridView?.onDropView()
57
70
  return super.onDropViewInstance(view)
58
71
  }
59
72
 
60
73
  protected override fun prepareToRecycleView(reactContext: ThemedReactContext, view: View): View? {
61
- super.prepareToRecycleView(reactContext, view)
62
- val hybridView = getHybridView(view)
74
+ val preparedView = super.prepareToRecycleView(reactContext, view)
75
+ ?: return null
76
+ val holder = getHybridViewHolder(preparedView)
63
77
  ?: return null
78
+ val hybridView = holder.hybridView
79
+ holder.lastState = null
64
80
 
65
81
  @Suppress("USELESS_IS_CHECK")
66
82
  if (hybridView is RecyclableView) {
@@ -74,7 +90,7 @@ public class HybridSkeletonManager: SimpleViewManager<View>() {
74
90
  }
75
91
  }
76
92
 
77
- private fun getHybridView(view: View): HybridSkeleton? {
78
- return view.getTag(associated_hybrid_view_tag) as? HybridSkeleton
93
+ private fun getHybridViewHolder(view: View): HybridViewHolder? {
94
+ return view.getTag(associated_hybrid_view_tag) as? HybridViewHolder
79
95
  }
80
96
  }
@@ -14,10 +14,10 @@ internal class HybridSkeletonStateUpdater {
14
14
  companion object {
15
15
  /**
16
16
  * Updates the props for [view] through C++.
17
- * The [state] prop is expected to contain [view]'s props as wrapped Fabric state.
17
+ * The [newState] prop is expected to contain [view]'s props as wrapped Fabric state.
18
18
  */
19
19
  @Suppress("KotlinJniMissingFunction")
20
20
  @JvmStatic
21
- external fun updateViewProps(view: HybridSkeletonSpec, state: StateWrapper)
21
+ external fun updateViewProps(view: HybridSkeletonSpec, newState: StateWrapper, oldState: StateWrapper?)
22
22
  }
23
23
  }