Bridgeless: Fix unstable_hasComponent

Summary:
unstable_hasComponent(component) first registers the component with react native, if possible. Then, it returns you whether registration succeeded.

## Problem
For unregistered components, the initial call returns false. But, all subsequent calls return true.

The reason why: After the initial call fails, react native registers unimplemented view under that component name. So, all subsequent calls return true.

## Solution
Just record the initial loopup result. And always return that initial lookup result from this method.

Changelog: [iOS][Fixed] Fix bug: unstable_hasComponent(*) = true for unregistered components for n > 1th call.

Reviewed By: yungsters

Differential Revision: D73127468

fbshipit-source-id: ee30bde486a6bb970f40f654c65a8946452f49b3
This commit is contained in:
Ramanpreet Nara
2025-04-17 14:33:26 -07:00
committed by Facebook GitHub Bot
parent f144b53122
commit fa9d082747
2 changed files with 35 additions and 37 deletions
@@ -48,12 +48,6 @@ void RCTInstallNativeComponentRegistryBinding(facebook::jsi::Runtime &runtime);
*/
- (void)registerComponentViewClass:(Class<RCTComponentViewProtocol>)componentViewClass;
/**
* Registers component if there is a matching class. Returns true if it matching class is found or the component has
* already been registered, false otherwise.
*/
- (BOOL)registerComponentIfPossible:(const std::string &)componentName;
/**
* Creates a component view with given component handle.
*/
@@ -45,12 +45,18 @@
using namespace facebook;
using namespace facebook::react;
@interface RCTComponentViewFactory ()
- (void)_registerComponentIfPossible:(const std::string &)name;
- (BOOL)_wasComponentRegistered:(const std::string &)name;
@end
// Allow JS runtime to register native components as needed. For static view configs.
void RCTInstallNativeComponentRegistryBinding(facebook::jsi::Runtime &runtime)
{
auto hasComponentProvider = [](const std::string &name) -> bool {
return [[RCTComponentViewFactory currentComponentViewFactory]
registerComponentIfPossible:componentNameByReactViewName(name)];
auto globalComponentViewFactory = [RCTComponentViewFactory currentComponentViewFactory];
[globalComponentViewFactory _registerComponentIfPossible:name];
return [globalComponentViewFactory _wasComponentRegistered:name];
};
bindHasComponentProvider(runtime, std::move(hasComponentProvider));
}
@@ -62,7 +68,7 @@ static Class<RCTComponentViewProtocol> RCTComponentViewClassWithName(const char
@implementation RCTComponentViewFactory {
std::unordered_map<ComponentHandle, RCTComponentViewClassDescriptor> _componentViewClasses;
std::unordered_set<std::string> _registeredComponentsNames;
std::unordered_map<std::string, bool> _registrationStatusMap;
ComponentDescriptorProviderRegistry _providerRegistry;
std::shared_mutex _mutex;
}
@@ -81,7 +87,7 @@ static Class<RCTComponentViewProtocol> RCTComponentViewClassWithName(const char
componentViewFactory->_providerRegistry.setComponentDescriptorProviderRequest(
[](ComponentName requestedComponentName) {
[componentViewFactory registerComponentIfPossible:requestedComponentName];
[componentViewFactory _registerComponentIfPossible:requestedComponentName];
});
});
@@ -106,11 +112,16 @@ static Class<RCTComponentViewProtocol> RCTComponentViewClassWithName(const char
#pragma clang diagnostic pop
}
- (BOOL)registerComponentIfPossible:(const std::string &)name
- (BOOL)_wasComponentRegistered:(const std::string &)name
{
if (_registeredComponentsNames.find(name) != _registeredComponentsNames.end()) {
// Component has already been registered.
return YES;
auto registrationResult = _registrationStatusMap.find(name);
return registrationResult != _registrationStatusMap.end() && (registrationResult->second);
}
- (void)_registerComponentIfPossible:(const std::string &)name
{
if (_registrationStatusMap.find(name) != _registrationStatusMap.end()) {
return;
}
// Paper name: we prepare this variables to warn the user
@@ -122,7 +133,7 @@ static Class<RCTComponentViewProtocol> RCTComponentViewClassWithName(const char
Class<RCTComponentViewProtocol> klass = RCTComponentViewClassWithName(name.c_str());
if (klass) {
[self registerComponentViewClass:klass];
return YES;
return;
}
// Fallback 2: Ask the provider and check in the dictionary provided
@@ -133,7 +144,7 @@ static Class<RCTComponentViewProtocol> RCTComponentViewClassWithName(const char
klass = self.thirdPartyFabricComponentsProvider.thirdPartyFabricComponents[objcName];
if (klass) {
[self registerComponentViewClass:klass];
return YES;
return;
}
}
@@ -153,12 +164,13 @@ static Class<RCTComponentViewProtocol> RCTComponentViewClassWithName(const char
auto componentHandle = reinterpret_cast<ComponentHandle>(componentName);
auto constructor = [RCTLegacyViewManagerInteropComponentView componentDescriptorProvider].constructor;
[self _addDescriptorToProviderRegistry:ComponentDescriptorProvider{
componentHandle, componentName, flavor, constructor}];
auto provider = ComponentDescriptorProvider{componentHandle, componentName, flavor, constructor};
_providerRegistry.add(provider);
_componentViewClasses[componentHandle] =
[self _componentViewClassDescriptorFromClass:[RCTLegacyViewManagerInteropComponentView class]];
return YES;
_registrationStatusMap.insert({provider.name, true});
return;
}
// Fallback 4: use <UnimplementedView> if component doesn't exist.
@@ -166,15 +178,12 @@ static Class<RCTComponentViewProtocol> RCTComponentViewClassWithName(const char
auto componentName = ComponentName{flavor->c_str()};
auto componentHandle = reinterpret_cast<ComponentHandle>(componentName);
auto constructor = [RCTUnimplementedViewComponentView componentDescriptorProvider].constructor;
auto provider = ComponentDescriptorProvider{componentHandle, componentName, flavor, constructor};
[self _addDescriptorToProviderRegistry:ComponentDescriptorProvider{
componentHandle, componentName, flavor, constructor}];
_providerRegistry.add(provider);
_componentViewClasses[componentHandle] =
[self _componentViewClassDescriptorFromClass:[RCTUnimplementedViewComponentView class]];
// No matching class exists for `name`.
return NO;
_registrationStatusMap.insert({provider.name, false});
}
- (void)registerComponentViewClass:(Class<RCTComponentViewProtocol>)componentViewClass
@@ -182,23 +191,18 @@ static Class<RCTComponentViewProtocol> RCTComponentViewClassWithName(const char
RCTAssert(componentViewClass, @"RCTComponentViewFactory: Provided `componentViewClass` is `nil`.");
std::unique_lock lock(_mutex);
auto componentDescriptorProvider = [componentViewClass componentDescriptorProvider];
_componentViewClasses[componentDescriptorProvider.handle] =
[self _componentViewClassDescriptorFromClass:componentViewClass];
[self _addDescriptorToProviderRegistry:componentDescriptorProvider];
auto provider = [componentViewClass componentDescriptorProvider];
_componentViewClasses[provider.handle] = [self _componentViewClassDescriptorFromClass:componentViewClass];
_providerRegistry.add(provider);
_registrationStatusMap.insert({provider.name, true});
auto supplementalComponentDescriptorProviders = [componentViewClass supplementalComponentDescriptorProviders];
for (const auto &provider : supplementalComponentDescriptorProviders) {
[self _addDescriptorToProviderRegistry:provider];
for (const auto &supplementalProvider : supplementalComponentDescriptorProviders) {
_providerRegistry.add(supplementalProvider);
_registrationStatusMap.insert({supplementalProvider.name, true});
}
}
- (void)_addDescriptorToProviderRegistry:(const ComponentDescriptorProvider &)provider
{
_registeredComponentsNames.insert(provider.name);
_providerRegistry.add(provider);
}
- (RCTComponentViewDescriptor)createComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle
{
RCTAssertMainQueue();