Skip to content

Commit cb95b3f

Browse files
committed
Fix updateEventEmitter clobbering deferred text styles and preserve EventEmitter key in traitCollectionDidChange
- updateEventEmitter: use _pendingDefaultTextAttributes as base when it exists, so deferred text-style changes from updateProps are not lost - traitCollectionDidChange: preserve EventEmitter key in the non-composing branch (was already preserved in the composing branch) - RCTUITextView/RCTUITextField: replace @"EventEmitter" string literal with local constant kRCTEventEmitterAttributeKey for consistency - Remove duplicate test file from React/Tests/TextInput/ (canonical copy is in rn-tester/RNTesterUnitTests/)
1 parent 676cb64 commit cb95b3f

4 files changed

Lines changed: 20 additions & 722 deletions

File tree

packages/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
#import <React/RCTBackedTextInputDelegateAdapter.h>
1414
#import <React/RCTTextAttributes.h>
1515

16+
// Must match RCTAttributedStringEventEmitterKey in RCTAttributedTextUtils.h
17+
// (cannot import directly — Libraries/Text must not depend on ReactCommon).
18+
static NSString *const kRCTEventEmitterAttributeKey = @"EventEmitter";
19+
1620
@implementation RCTUITextView {
1721
UILabel *_placeholderView;
1822
UITextView *_detachedTextView;
@@ -138,7 +142,7 @@ - (void)setDefaultTextAttributes:(NSDictionary<NSAttributedStringKey, id> *)defa
138142
// Strip attributes that interfere with UIKit's IME composition underline rendering.
139143
// Only remove no-op defaults; preserve user-specified values.
140144
NSMutableDictionary *typingAttrs = [defaultTextAttributes mutableCopy];
141-
[typingAttrs removeObjectForKey:@"EventEmitter"];
145+
[typingAttrs removeObjectForKey:kRCTEventEmitterAttributeKey];
142146
NSShadow *shadow = typingAttrs[NSShadowAttributeName];
143147
if (shadow && CGSizeEqualToSize(shadow.shadowOffset, CGSizeZero) && shadow.shadowBlurRadius == 0) {
144148
[typingAttrs removeObjectForKey:NSShadowAttributeName];

packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
#import <React/RCTUtils.h>
1313
#import <React/UIView+React.h>
1414

15+
// Must match RCTAttributedStringEventEmitterKey in RCTAttributedTextUtils.h
16+
// (cannot import directly — Libraries/Text must not depend on ReactCommon).
17+
static NSString *const kRCTEventEmitterAttributeKey = @"EventEmitter";
18+
1519
@implementation RCTUITextField {
1620
RCTBackedTextFieldDelegateAdapter *_textInputDelegateAdapter;
1721
NSDictionary<NSAttributedStringKey, id> *_defaultTextAttributes;
@@ -99,7 +103,7 @@ - (void)setDefaultTextAttributes:(NSDictionary<NSAttributedStringKey, id> *)defa
99103
// clear background) — preserve user-specified values.
100104
// EventEmitter is a React-internal attribute (NSData wrapping C++ weak_ptr).
101105
NSMutableDictionary *uikitAttrs = [defaultTextAttributes mutableCopy];
102-
[uikitAttrs removeObjectForKey:@"EventEmitter"];
106+
[uikitAttrs removeObjectForKey:kRCTEventEmitterAttributeKey];
103107
NSShadow *shadow = uikitAttrs[NSShadowAttributeName];
104108
if (shadow && CGSizeEqualToSize(shadow.shadowOffset, CGSizeZero) && shadow.shadowBlurRadius == 0) {
105109
[uikitAttrs removeObjectForKey:NSShadowAttributeName];

packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,12 @@ - (void)updateEventEmitter:(const EventEmitter::Shared &)eventEmitter
117117
{
118118
[super updateEventEmitter:eventEmitter];
119119

120+
// Use pending attributes as the base if they exist (e.g., from a prior updateProps
121+
// that deferred during composition), to avoid clobbering deferred text-style changes.
120122
NSMutableDictionary<NSAttributedStringKey, id> *defaultAttributes =
121-
[_backedTextInputView.defaultTextAttributes mutableCopy];
123+
_pendingDefaultTextAttributes
124+
? [_pendingDefaultTextAttributes mutableCopy]
125+
: [_backedTextInputView.defaultTextAttributes mutableCopy];
122126

123127
defaultAttributes[RCTAttributedStringEventEmitterKey] = RCTWrapEventEmitter(_eventEmitter);
124128

@@ -173,6 +177,11 @@ - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
173177
_needsUpdateDefaultTextAttributes = YES;
174178
_pendingDefaultTextAttributes = [attributes copy];
175179
} else {
180+
// Preserve the event emitter key since RCTNSTextAttributesFromTextAttributes does not include it.
181+
id emitter = _backedTextInputView.defaultTextAttributes[RCTAttributedStringEventEmitterKey];
182+
if (emitter) {
183+
attributes[RCTAttributedStringEventEmitterKey] = emitter;
184+
}
176185
_backedTextInputView.defaultTextAttributes = attributes;
177186
}
178187
}

0 commit comments

Comments
 (0)