From 613a5a75977d84333df7cbd5701e01a7ab5a3385 Mon Sep 17 00:00:00 2001 From: Wojciech Lewicki Date: Fri, 5 Jan 2024 07:03:31 -0800 Subject: [PATCH] feat: make view recycling optional on iOS (#35378) Summary: This PR resolves the potential problem of misconfiguration of components after being recycled. Some of them have custom, sometimes native (e.g. connected to VCs) logic that messes up with the concept of recycling. bypass-github-export-checks ## Changelog Added `shouldBeRecycled` field checking to `RCTComponentViewClassDescriptor `, a check for it in `_enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandle componentViewDescriptor:(RCTComponentViewDescriptor)componentViewDescriptor` method, and a default implementation in `RCTComponentViewDescriptor` returning `YES` in order not to change the default behavior. [iOS] [Added] - Add `shouldBeRecycled` method on `iOS`. Pull Request resolved: https://github.com/facebook/react-native/pull/35378 Test Plan: Override this method in your custom `componentView` and see that the component is not recycled. Reviewed By: javache Differential Revision: D41381683 Pulled By: cipolleschi fbshipit-source-id: 10fd1e88f99b3608767c0b57fad462837924f02a --- .../React/Fabric/Mounting/RCTComponentViewClassDescriptor.h | 5 +++++ .../React/Fabric/Mounting/RCTComponentViewDescriptor.h | 1 + .../React/Fabric/Mounting/RCTComponentViewFactory.mm | 4 ++++ .../React/Fabric/Mounting/RCTComponentViewRegistry.mm | 2 +- 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/react-native/React/Fabric/Mounting/RCTComponentViewClassDescriptor.h b/packages/react-native/React/Fabric/Mounting/RCTComponentViewClassDescriptor.h index b03a4642a98..3bc159229f7 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTComponentViewClassDescriptor.h +++ b/packages/react-native/React/Fabric/Mounting/RCTComponentViewClassDescriptor.h @@ -27,6 +27,11 @@ class RCTComponentViewClassDescriptor final { */ bool observesMountingTransactionWillMount{false}; bool observesMountingTransactionDidMount{false}; + + /* + * Whether the component can be recycled or not + */ + bool shouldBeRecycled{true}; }; NS_ASSUME_NONNULL_END diff --git a/packages/react-native/React/Fabric/Mounting/RCTComponentViewDescriptor.h b/packages/react-native/React/Fabric/Mounting/RCTComponentViewDescriptor.h index 3ca65d18fcb..fd1c4b9feb0 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTComponentViewDescriptor.h +++ b/packages/react-native/React/Fabric/Mounting/RCTComponentViewDescriptor.h @@ -29,6 +29,7 @@ class RCTComponentViewDescriptor final { */ bool observesMountingTransactionWillMount{false}; bool observesMountingTransactionDidMount{false}; + bool shouldBeRecycled{true}; }; inline bool operator==(const RCTComponentViewDescriptor &lhs, const RCTComponentViewDescriptor &rhs) diff --git a/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.mm b/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.mm index 51545202a3b..ebd9d5a6e9e 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.mm +++ b/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.mm @@ -96,6 +96,9 @@ static Class RCTComponentViewClassWithName(const char (bool)class_respondsToSelector(viewClass, @selector(mountingTransactionWillMount:withSurfaceTelemetry:)), .observesMountingTransactionDidMount = (bool)class_respondsToSelector(viewClass, @selector(mountingTransactionDidMount:withSurfaceTelemetry:)), + .shouldBeRecycled = [viewClass respondsToSelector:@selector(shouldBeRecycled)] + ? (bool)[viewClass performSelector:@selector(shouldBeRecycled)] + : true, }; #pragma clang diagnostic pop } @@ -210,6 +213,7 @@ static Class RCTComponentViewClassWithName(const char .view = [viewClass new], .observesMountingTransactionWillMount = componentViewClassDescriptor.observesMountingTransactionWillMount, .observesMountingTransactionDidMount = componentViewClassDescriptor.observesMountingTransactionDidMount, + .shouldBeRecycled = componentViewClassDescriptor.shouldBeRecycled, }; } diff --git a/packages/react-native/React/Fabric/Mounting/RCTComponentViewRegistry.mm b/packages/react-native/React/Fabric/Mounting/RCTComponentViewRegistry.mm index e78e8ae5334..74ebc3160ea 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTComponentViewRegistry.mm +++ b/packages/react-native/React/Fabric/Mounting/RCTComponentViewRegistry.mm @@ -107,7 +107,7 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024; RCTAssertMainQueue(); auto &recycledViews = _recyclePool[componentHandle]; - if (recycledViews.size() > RCTComponentViewRegistryRecyclePoolMaxSize) { + if (recycledViews.size() > RCTComponentViewRegistryRecyclePoolMaxSize || !componentViewDescriptor.shouldBeRecycled) { return; }