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
This commit is contained in:
Wojciech Lewicki
2024-01-05 07:03:31 -08:00
committed by Facebook GitHub Bot
parent cfeb43eaa2
commit 613a5a7597
4 changed files with 11 additions and 1 deletions
@@ -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
@@ -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)
@@ -96,6 +96,9 @@ static Class<RCTComponentViewProtocol> 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<RCTComponentViewProtocol> RCTComponentViewClassWithName(const char
.view = [viewClass new],
.observesMountingTransactionWillMount = componentViewClassDescriptor.observesMountingTransactionWillMount,
.observesMountingTransactionDidMount = componentViewClassDescriptor.observesMountingTransactionDidMount,
.shouldBeRecycled = componentViewClassDescriptor.shouldBeRecycled,
};
}
@@ -107,7 +107,7 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
RCTAssertMainQueue();
auto &recycledViews = _recyclePool[componentHandle];
if (recycledViews.size() > RCTComponentViewRegistryRecyclePoolMaxSize) {
if (recycledViews.size() > RCTComponentViewRegistryRecyclePoolMaxSize || !componentViewDescriptor.shouldBeRecycled) {
return;
}