Skip to content

TextInput onChangeText called automatically for maxLength and multiline #36494

Description

@shrihari1999

Description

Consider a TextInput that has maxLength set and multiline true. When the text input's initial value's length is more than half the maxLength value, onChangeText is automatically called on render.

I am unable to reproduce this in a snack, happens on multiple iOS devices I've tried. No issue with android.

React Native Version

0.71.3

Output of npx react-native info

System:
OS: Linux 4.15 Ubuntu 16.04.7 LTS (Xenial Xerus)
CPU: (4) x64 Intel(R) Core(TM) i5-2320 CPU @ 3.00GHz
Memory: 3.66 GB / 7.76 GB
Shell: 4.3.48 - /bin/bash
Binaries:
Node: 16.17.0 - ~/.config/nvm/versions/node/v16.17.0/bin/node
Yarn: 1.22.19 - ~/.config/nvm/versions/node/v16.17.0/bin/yarn
npm: 8.15.0 - ~/.config/nvm/versions/node/v16.17.0/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
Android SDK:
API Levels: 28, 29, 30, 31
Build Tools: 28.0.3, 29.0.2, 29.0.3
System Images: android-27 | Google Play Intel x86 Atom
Android NDK: Not Found
IDEs:
Android Studio: Not Found
Languages:
Java: 1.8.0_251 - /home/shrihari/jdk1.8.0_251/bin/javac
npmPackages:
@react-native-community/cli: Not Found
react: 18.2.0 => 18.2.0
react-native: 0.71.3 => 0.71.3
npmGlobalPackages:
react-native: Not Found

Steps to reproduce

  1. Use a TextInput with maxLength={10} and multiline={true}
  2. Set the initial state of textinput's value to a string of length more than 5.
  3. Observe onChangeText is automatically called with wrong text parameter

Snack, code example, screenshot, or link to a repository

import React, { useState } from 'react'
import { SafeAreaView, TextInput } from 'react-native'

export default function EditProfileFieldScreen({ route }) {
    // const [fieldValue, setFieldValue] = useState('abcde') // works correctly
    const [fieldValue, setFieldValue] = useState('abcdef') // does not work correctly
    console.log('initial length', fieldValue.length)

    const handleTextChange = (text) => {
        console.log('text change called', text.length)
        setFieldValue(text)
    }


    return (
        <SafeAreaView style={{flex: 1, backgroundColor: 'white'}}>
            <TextInput
                value={fieldValue}
                onChangeText={handleTextChange}
                maxLength={10}
                multiline={true}
            />

        </SafeAreaView>
    )
}

I am using Expo SDK 48. I am also running the lastest version of Expo GO

Activity

  1. github-actions commented on Mar 16, 2023

    @github-actions
    ⚠️ Newer Version of React Native is Available!
    ℹ️ You are on a supported minor version, but it looks like there's a newer patch available. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases.
  2. Fb9027 commented on Mar 16, 2023

    @Fb9027
  3. shrihari1999 commented on Mar 17, 2023

    @shrihari1999
    Author

    warning Newer Version of React Native is Available!
    information_source You are on a supported minor version, but it looks like there's a newer patch available. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases.

    I tried upgrading to the latest version (0.71.4). I can confirm that the issue still persists. Someone please help!

  4. SrikrishnaParthasarathy commented on Mar 18, 2023

    @SrikrishnaParthasarathy

    Hello, has anyone found a solution? I'm having the same problem.

  5. remanation commented on Mar 23, 2023

    @remanation

    We also experience this. I would be really nice to get fixed.

  6. remanation commented on Mar 23, 2023

    @remanation

    I found out it was introduces in this commit: 112bfee
    I tried to revert the changes locally and that fixed the issue. For now we will apply a patch in our repo the reverts the changes.
    I'm not really sure why the changes cause the issue. @s77rt could you maybe have a look as it seems like you did the changes.

  7. shrihari1999 commented on Mar 23, 2023

    @shrihari1999
    Author

    In case anyone doesn't want to change the source code, I did a dirty settimeout workaround, that basically shields the component from updating for the first 300ms.

    Someone please fix this officially!!

  8. s77rt commented on Mar 23, 2023

    @s77rt
    Contributor

    @remanation I have been looking into this. I think that the bug existed for long but instead of having onChangeText fire it was onSelectionChange. In the new version (0.71) every time onSelectonChange is about to fire, we will first fire onChangeText (to switch the order).

    So the real bug to investigate is: Why onSelectionChange fires at render on a multiline TextInput.
    This is reproducible even on older versions (before the linked commit): https://snack.expo.dev/uy58XKz_S?platform=ios

  9. smhaltnsk commented on Mar 24, 2023

    @smhaltnsk

    0.70.6 not have this bug, I upgraded to 0.71.4 and now I have this bug

  10. jzaleski commented on Mar 26, 2023

    @jzaleski

    Just spent this evening chasing down this issue, we're on 0.71.4, and then I stumbled across this issue. Bah. Appreciate you guys looking into this!

  11. jzaleski commented on Mar 26, 2023

    @jzaleski

    In case anyone doesn't want to change the source code, I did a dirty settimeout workaround, that basically shields the component from updating for the first 300ms.

    Someone please fix this officially!!

    As a temporary solution until the root-cause can be addressed I did the the same thing -- thanks for the recommendation @shrihari1999.

  12. 35 remaining items

  13. nicolasdevienne commented on Aug 21, 2023

    @nicolasdevienne

    I confirm the issue. Indeed, when using multiline, OnChangeText starts to fire a large number of times. Disabling this setting resolves the issue. 0.72.3 react. this happens in a modal window on ios

    same with version 0.72.4

  14. sergey-shablenko commented on Aug 27, 2023

    @sergey-shablenko

    Found a workaround, just in case anyone needs it
    You can use

    <TextInput 
      multiline
      numberOfLines={numberOfLines}
      defaultValue={value}
      onEndEditing={(e) => onChangeText(e.nativeEvent.text)}
    />

    may not fit everyone, but at least does not go into infinite loop

  15. douglasjunior commented on Aug 28, 2023

    @douglasjunior

    Updating the behavior mentioned here: #36494 (comment)

    version description
    <= 0.71.7 ⚠️ Changing value call onChangeText, then if you change value again the onChangeText will be called once more, creating an infinity loop. If you don't change value on every onChangeText, then will be fine.
    >= 0.71.8 && <= 0.71.12 🚨 Changing value call onChangeText, then if you change value again the onChangeText will not be called, but if you type some new char the onChangeText will not be fired in the first time.
    = 0.71.13 (latest) ⚠️ Same as RN <= 0.71.7
    >= 0.72.0 ⚠️ Same as RN <= 0.71.7
  16. josef256 commented on Sep 3, 2023

    @josef256

    same issue on IOS with multiline set to true
    on rn 0.72.3
    edit : as a workaround using defaultValue instead of value seems to fix it

    <TextInput 
    defaultValue={value}
    />
    
  17. ukcasso commented on Sep 25, 2023

    @ukcasso

    If you use a pre-release version RN, it is for temporary use.
    You try this example code.
    Fix automatically occur text, also can paste text.

    import { Platform, TextInput } from 'react-native';
    const [value, setValue] = useState(props.value || '');
    const maxLength = 100;
    
    <TextInput
      onChangeText={(newText) => {
        if (Platform.OS === 'ios' &&
            value &&
            maxLength &&
            value.length > (maxLength / 2) &&
            newText.length - value.length >= maxLength - value.length) {
          return;
        }
        
        setValue(newText);
      }}
      value={value}
      maxLength={maxLength}
      multiline
    />
    
  18. matheusbaumgart commented on Feb 4, 2024

    @matheusbaumgart

    Still an issue on 0.73.2

  19. added a commit that references this issue on Apr 17, 2024
  20. jackylu0124 commented on Aug 29, 2024

    @jackylu0124

    just encountered this problem yesterday. Took me a while to figure out what was going on. A simple but dirty solution is to use the useRef hook. Set the initial hook to 0. in the onChangeText method, check to see if the useRef value is 0; if it is, do not execute the callback, otherwise execute the callback. From then on, keep incrementing the useRef value. This will persist across state changes

    Could you please provide a reference of your alternative solution? In my case, not only is onChangeText being called repeatedly, but its value is also changed. For example, in my case, I have maxLength={8} and if I type "Abcde" and then programmtically set the state to empty string and navigate to another screen; and then coming back to the screen and programmatically setting the state to "Abcde" again, the text input then incorrectly renders the string "AbcAbcde". I have encountered very similar behavior to this issue: #44566

    Thanks for your help in advance!

  21. mohammad-goldast commented on Dec 1, 2024

    @mohammad-goldast

    Still have the issue on React-Native 0.76.

    onContentSizeChange called automatically and infinite times. The issue is related to multiline prop, when I remove the prop, everything goes well.

    <TextInput
      defaultValue={journalData.content}
      ref={contentInputRef}
      style={[styles.contentInput, {height: contentInputHeight}]}
      value={journalData.content}
      onContentSizeChange={(event) => {
        console.log(event.nativeEvent.contentSize.height);
        setContentInputHeight(event.nativeEvent.contentSize.height + 40);
      }}
      onChangeText={(content) => setJournalData((prev) => ({...prev, content}))}
      onFocus={() => {
        if (journalData.title?.length === 0) {
          titleInputRef.current?.focus();
        } else {
          focusedInput.current = 'content';
        }
      }}
      scrollEnabled={false}
      returnKeyType="next"
      underlineColorAndroid="transparent"
      onSelectionChange={({nativeEvent}) => {
        selectionState.current = nativeEvent.selection;
      }}
      autoCorrect
      multiline
    />
  22. phamha98 commented on Apr 11, 2025

    @phamha98

    forced call back
    react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m

    - (void)textViewDidChange:(__unused UITextView *)textView
    {
        NSLog(@"textViewDidChange: %@", textView);
    
      if (_ignoreNextTextInputCall && [_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
        _ignoreNextTextInputCall = NO;
        [_backedTextInputView.textInputDelegate textInputDidChange]; // <-add force
        return;
      }
      _textDidChangeIsComing = NO;
      [_backedTextInputView.textInputDelegate textInputDidChange];
    }
    - (void)textViewDidChangeSelection:(__unused UITextView *)textView
    {
  23. josiext commented on Apr 21, 2025

    @josiext

    So this is not solved yer?

  24. dcdavidheisnam commented on Aug 7, 2025

    @dcdavidheisnam

    Still happening on 0.79.5 with onChange/onChangeText and multiline.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Component: TextInputRelated to the TextInput component.Impact: RegressionDescribes a behavior that used to work on a prior release, but stopped working recently.Issue: Author Provided ReproThis issue can be reproduced in Snack or an attached project.Needs: Triage 🔍Platform: iOSiOS applications.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions