From 0aa017dc10c3629df294371744d64a5a8a054151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Fri, 11 Oct 2024 14:10:06 +0200 Subject: [PATCH 1/2] fix: don't update text input text directly in setTextAndSelection --- .../TextInput/RCTTextInputComponentView.mm | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 8c532d85502b..63b5868988f7 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -482,10 +482,9 @@ - (void)setTextAndSelection:(NSInteger)eventCount if (value && ![value isEqualToString:_backedTextInputView.attributedText.string]) { NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:value attributes:_backedTextInputView.defaultTextAttributes]; - [self _setAttributedString:attributedString]; - [self _updateState]; + [self _updateStateWithString:attributedString]; } - + UITextPosition *startPosition = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument offset:start]; UITextPosition *endPosition = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument @@ -493,8 +492,10 @@ - (void)setTextAndSelection:(NSInteger)eventCount if (startPosition && endPosition) { UITextRange *range = [_backedTextInputView textRangeFromPosition:startPosition toPosition:endPosition]; + // _updateStateWithString executes any state updates sync, so its safe to update the selection as the attributedString is already updated! [_backedTextInputView setSelectedTextRange:range notifyDelegate:NO]; } + _comingFromJS = NO; } @@ -614,17 +615,27 @@ - (void)handleInputAccessoryDoneButton } - (void)_updateState +{ + NSAttributedString *attributedString = _backedTextInputView.attributedText; + [self _updateStateWithString:attributedString]; +} + +- (void)_updateStateWithString:(NSAttributedString*)attributedString { if (!_state) { return; } - NSAttributedString *attributedString = _backedTextInputView.attributedText; auto data = _state->getData(); _lastStringStateWasUpdatedWith = attributedString; data.attributedStringBox = RCTAttributedStringBoxFromNSAttributedString(attributedString); _mostRecentEventCount += _comingFromJS ? 0 : 1; data.mostRecentEventCount = _mostRecentEventCount; - _state->updateState(std::move(data)); + const auto &textInputEventEmitter = static_cast(*_eventEmitter); + // When the textInputDidChange gets called, the text is already updated + // in the UI. We execute the state update synchronously so that the layout gets calculated immediately. + textInputEventEmitter.experimental_flushSync([state = _state, data = std::move(data)]() mutable { + state->updateState(std::move(data)); + }); } - (AttributedString::Range)_selectionRange From dd5b017d736f8148ae3ae6484134b19eb4fe82f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Fri, 11 Oct 2024 15:20:31 +0200 Subject: [PATCH 2/2] cleanup lines --- .../ComponentViews/TextInput/RCTTextInputComponentView.mm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 63b5868988f7..10eda031ccf1 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -484,7 +484,7 @@ - (void)setTextAndSelection:(NSInteger)eventCount [[NSAttributedString alloc] initWithString:value attributes:_backedTextInputView.defaultTextAttributes]; [self _updateStateWithString:attributedString]; } - + UITextPosition *startPosition = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument offset:start]; UITextPosition *endPosition = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument @@ -495,7 +495,6 @@ - (void)setTextAndSelection:(NSInteger)eventCount // _updateStateWithString executes any state updates sync, so its safe to update the selection as the attributedString is already updated! [_backedTextInputView setSelectedTextRange:range notifyDelegate:NO]; } - _comingFromJS = NO; }