Files
react-native/packages
Deepanshu.shuklaandFacebook GitHub Bot d88dd14507 fix: onChangeText should be called only once if changed via js in a multiline textinput (#45401)
Summary:
Fixes [44566](https://github.com/facebook/react-native/issues/44566)

Issue was onChangeText was called 5-6 times if maxLength was set in a multiline component and TextInput Value was changed via state update.
`if (_maxLength) {
    NSInteger allowedLength = MAX(
        _maxLength.integerValue - (NSInteger)backedTextInputView.attributedText.string.length + (NSInteger)range.length,
        0);

    if (text.length > allowedLength) {
      // If we typed/pasted more than one character, limit the text inputted.
      if (text.length > 1) {
        if (allowedLength > 0) {
          // make sure unicode characters that are longer than 16 bits (such as emojis) are not cut off
          NSRange cutOffCharacterRange = [text rangeOfComposedCharacterSequenceAtIndex:allowedLength - 1];
          if (cutOffCharacterRange.location + cutOffCharacterRange.length > allowedLength) {
            // the character at the length limit takes more than 16bits, truncation should end at the character before
            allowedLength = cutOffCharacterRange.location;
          }
        }
        // Truncate the input string so the result is exactly maxLength
        NSString *limitedString = [text substringToIndex:allowedLength];
        NSMutableAttributedString *newAttributedText = [backedTextInputView.attributedText mutableCopy];
        // Apply text attributes if original input view doesn't have text.
        if (backedTextInputView.attributedText.length == 0) {
          newAttributedText = [[NSMutableAttributedString alloc]
              initWithString:[self.textAttributes applyTextAttributesToText:limitedString]
                  attributes:self.textAttributes.effectiveTextAttributes];
        } else {
          [newAttributedText replaceCharactersInRange:range withString:limitedString];
        }
        backedTextInputView.attributedText = newAttributedText;
        _predictedText = newAttributedText.string;

        // Collapse selection at end of insert to match normal paste behavior.
        UITextPosition *insertEnd = [backedTextInputView positionFromPosition:backedTextInputView.beginningOfDocument
                                                                       offset:(range.location + allowedLength)];
        [backedTextInputView setSelectedTextRange:[backedTextInputView textRangeFromPosition:insertEnd
                                                                                  toPosition:insertEnd]
                                   notifyDelegate:YES];

        [self textInputDidChange];
      }

      return nil; // Rejecting the change.
    }}`

This is the original code snippet.
It was happening because of wrong check of maxLength with text length `if (text.length > allowedLength)` this  should be
`(text.length > _maxLength.integerValue)`  and `if (allowedLength <= 0)`  we should not change the string and fire `textInputDidChange`

## Changelog:
[IOS] [FIXED] : Fixing maxLength check which was firing onChange multiple times

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: https://github.com/facebook/react-native/pull/45401

Test Plan:
Tested in Ios
Ran yarn test
<img width="1661" alt="Screenshot 2024-07-12 at 1 00 28 PM" src="https://github.com/user-attachments/assets/fbad94a8-9989-4252-ad7d-e507d4eafd9e">

Reviewed By: sammy-SC

Differential Revision: D59911745

Pulled By: cipolleschi

fbshipit-source-id: 67410ec50d6a2415e568e1685699bfed02fd0a27
2024-07-31 04:06:14 -07:00
..