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 b03a4642a98123..3bc159229f7429 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 3ca65d18fcb1f1..fd1c4b9feb0cd8 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 51545202a3b5b9..ebd9d5a6e9e1d6 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.mm +++ b/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.mm @@ -96,6 +96,9 @@ - (RCTComponentViewClassDescriptor)_componentViewClassDescriptorFromClass:(Class (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 @@ - (RCTComponentViewDescriptor)createComponentViewWithComponentHandle:(facebook:: .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 e78e8ae53342fd..74ebc3160ea430 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTComponentViewRegistry.mm +++ b/packages/react-native/React/Fabric/Mounting/RCTComponentViewRegistry.mm @@ -107,7 +107,7 @@ - (void)_enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandl RCTAssertMainQueue(); auto &recycledViews = _recyclePool[componentHandle]; - if (recycledViews.size() > RCTComponentViewRegistryRecyclePoolMaxSize) { + if (recycledViews.size() > RCTComponentViewRegistryRecyclePoolMaxSize || !componentViewDescriptor.shouldBeRecycled) { return; }