Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't modify text in TextInput onChangeText callback on Android #23578

Open
rogerbright opened this issue Feb 21, 2019 · 71 comments · Fixed by fabOnReact/react-native-improved#16
Assignees
Labels
Bug Component: TextInput Related to the TextInput component. Platform: Android Android applications. Priority: Mid

Comments

@rogerbright
Copy link

rogerbright commented Feb 21, 2019

🐛 Bug Report

On Android, modifying the text within the onChange (or onChangeText) callback causes corruption of the text in the TextInput. (Not tested on iOS.)

For example, I'm trying to force all caps in my TextInput field. (This is to work around the react native autoCapitalize issue described here: #8932). So if a lowercase letter is entered, I change it to uppercase in the callback. Unfortunately, alternate keystrokes cause the entire previous text to be duplicated, but only if the entered keystroke was lowercase.

So, when forcing all caps, entering 1234 results in 1234 showing up; entering ABCD results in ABCD showing up; but entering abcd results in AABCAABCD.

This issue disappears if assigning a Math.random() key to the TextInput; but then of course so does the keyboard focus, making this an unacceptable workaround.

To Reproduce

See "Bug Report" and "Code Example" sections.

Expected Behavior

One should be able to modify the value inside TextInput's change callbacks, without the text becoming corrupted on the subsequent redisplay.

Code Example

export default class TestScr extends Component
{
  constructor(props)
  {
	super(props);
	this.state = { s6: '' };
  }
  textchg(event)
  {
	const {eventCount, target, text} = event.nativeEvent;
            // one would expect the contents of s6 to display after the redraw
	this.setState({ s6: text.toUpperCase() }); 
  }
  render()
  {
            // [same behavior if using onChangeText instead of onChange]
	let jsx0 = <View style={{ flexDirection: 'row' }} key={ 'hi' }>
		<TextInput placeholder={ 'hello' } value={ this.state.s6 }
			onChange={ (evt) => this.textchg(evt) }
			keyboardType={ 'default' } />
		</View>;
		
	return (<View style={{ backgroundColor: '#ffffff', padding: 10, }}>
		<ScrollView style={{ backgroundColor: '#ffffff', }}>
			{ jsx0 }
		</ScrollView>
	</View>);
  }
}

Environment

React Native Environment Info:
System:
OS: Linux 3.19 Ubuntu 14.04.3 LTS, Trusty Tahr
CPU: (4) x64 Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz
Memory: 626.14 MB / 15.38 GB
Shell: 6.18.01 - /bin/tcsh
Binaries:
Node: 8.11.3 - /usr/bin/node
npm: 5.6.0 - /usr/bin/npm
SDKs:
Android SDK:
API Levels: 10, 16, 23, 26, 27, 28
Build Tools: 19.1.0, 20.0.0, 21.1.2, 22.0.1, 23.0.1, 23.0.2, 26.0.3, 27.0.3, 28.0.2, 28.0.3
System Images: android-16 | ARM EABI v7a, android-23 | Intel x86 Atom_64, android-23 | Google APIs Intel x86 Atom_64, android-28 | Google APIs Intel x86 Atom
npmPackages:
react: 16.6.3 => 16.6.3
react-native: 0.58.6 => 0.58.6
npmGlobalPackages:
create-react-native-app: 1.0.0
react-native-cli: 2.0.1

@react-native-bot react-native-bot added Component: TextInput Related to the TextInput component. Platform: Android Android applications. Platform: Linux Building on Linux. Resolution: Old Version labels Feb 21, 2019
@react-native-bot
Copy link
Collaborator

It looks like you are using an older version of React Native. Please update to the latest release, v0.58 and verify if the issue still exists.

The "Resolution: Old Version" label will be removed automatically once you edit your original post with the results of running react-native info on a project using the latest release.

@rogerbright
Copy link
Author

Shouting into the wind! Hope the next person can use this as a basis for reporting the issue.

@hramos
Copy link
Contributor

hramos commented Feb 21, 2019

Sorry about that, but it's really important to make sure the issue is present in the latest release. We've had plenty of issues reported for bugs that have been fixed already.

For bug reports that have a minimal repro example, verifying on the latest version using a brand new project should not take a lot of effort.

@CatapultJesse
Copy link

@rogerbright
Copy link
Author

Still happening on latest release. I've updated the react-native info text in the description.

@ericlewis
Copy link
Contributor

@rogerbright is this happening in emulator, or on device?

@rogerbright
Copy link
Author

Tested just now on both an emulator (Nexus 5X API 28) and an actual device (Galaxy Tab A SM-T580). Same results on both.

@ericlewis
Copy link
Contributor

ericlewis commented Mar 2, 2019 via email

@rogerbright
Copy link
Author

rogerbright commented Mar 2, 2019

Sure.

https://snack.expo.io/By1LO7uUV

In the snack interface, the bug appears only on Android. Works correctly on iOS. Make sure you enter only lowercase letters.

@ericlewis
Copy link
Contributor

@rogerbright ty I will take a look at this!

@umair-khanzada
Copy link

I am facing the same issue.
I have explain the isuue here #23663 (comment)

@rogerbright
Copy link
Author

@umair-khanzada your issue seems different. In my case, the callbacks are being called just fine... it's what happens when I modify the text that makes things screwy.

@grabbou grabbou added Priority: Mid and removed Platform: Linux Building on Linux. labels Mar 19, 2019
@grabbou
Copy link
Contributor

grabbou commented Mar 19, 2019

Thanks @ericlewis for volunteering to fix it! I think this is really annoying and severe issue, but fortunately, it's not affecting many of our developers.

I am going to label this "mid-pri" while we are waiting for the PR.

@brentvatne
Copy link
Collaborator

this happens with setNativeProps directly as well: #24409

@thymikee
Copy link
Contributor

Can confirm this also happens on RN 0.59 as well. Looks like I didn't need to modify controlled text input in a while, but when I did I remember using an overlay over transparent uncontrolled text input that would display the transformed text. I'd say it's pretty serious bug (and pretty old, happens at least since 0.57).

@brentvatne
Copy link
Collaborator

here's a video of a repro: https://streamable.com/j4s4r

and the code (i tested this on 0.57 and 0.59):

import * as React from 'react';
import { Text, TextInput, View, StyleSheet } from 'react-native';

class ControlledInput extends React.Component {
  state = {
    value: '',
  };

  _handleChangeText = ({ nativeEvent: { text } }) => {
    this.setState({ value: text.toUpperCase() });
  };

  render() {
    return (
      <TextInput
        style={{
          width: 300,
          height: 50,
          padding: 10,
          backgroundColor: '#fff',
          borderWidth: 1,
          borderColor: '#eee',
          borderRadius: 2,
        }}
        onChange={this._handleChangeText}
        value={this.state.value}
      />
    );
  }
}

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <ControlledInput />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
});

and a snack: https://snack.expo.io/@notbrent/ranting-sandwich

@rogerbright
Copy link
Author

FWIW, I did end up solving this issue and many others. The solution starts with an "F" and rhymes with "butter." I realize that's not an option for everyone.

@thomazcapra
Copy link

FWIW, I did end up solving this issue and many others. The solution starts with an "F" and rhymes with "butter." I realize that's not an option for everyone.
Flutter doens't have this issue, right?

@rogerbright
Copy link
Author

Correct. I'm only six months in but so far my impression is that Flutter/Dart is mature, well thought-out, concise, and free of sanctimony.

@fabOnReact
Copy link
Contributor

I look again into this when I have more free time. I need to update my PR with the improvements included in #33468. For this reason #29070 is back to draft. We can not trigger setText in a ReactEditText. replace is used on purpose. Sorry for the delay in the development of this fix.

@mathbalduino
Copy link

Hey guys, any news on this one?

@danielcrk
Copy link

I guess not.

@hieucd04
Copy link

+1

@chancegraff
Copy link

chancegraff commented Jan 16, 2023

I'm seeing this on iOS but I suspect it's because of one of the packages I have installed.

  "dependencies": {
    "@react-navigation/bottom-tabs": "^6.5.3",
    "@react-navigation/native": "^6.1.2",
    "@react-navigation/native-stack": "^6.9.8",
    "expo": "^47.0.13",
    "expo-splash-screen": "~0.17.5",
    "expo-status-bar": "^1.4.2",
    "firebase": "^9.15.0",
    "immer": "^9.0.17",
    "jest": "^29.3.1",
    "react": "18.1.0",
    "react-dom": "18.1.0",
    "react-native": "0.70.5",
    "react-native-dotenv": "^3.4.7",
    "react-native-paper": "^5.1.3",
    "react-native-plaid-link-sdk": "^8.0.1",
    "react-native-safe-area-context": "^4.4.1",
    "react-native-screens": "^3.18.2",
    "react-native-text-input-mask": "^3.1.4",
    "react-native-web": "^0.18.10",
    "zustand": "^4.3.2"
  },
  "devDependencies": {
    "@babel/core": "^7.20.12",
    "@types/react": "^18.0.26",
    "@types/react-native": "^0.71.0",
    "babel-plugin-module-resolver": "^5.0.0",
    "babel-plugin-root-import": "^6.6.0",
    "expo-cli": "^6.1.0",
    "typescript": "^4.9.4"
  },

@saschagehlich
Copy link

@chancegraff I have the same issue on iOS, were you able to fix this?

@jcubic
Copy link

jcubic commented Jul 3, 2023

The same issue on Android in Emulator. It works fine for the Expo playground so don't use it to test.

Simple reproduction:

const ControlledComponent = () => {
  const [state, setState] = useState('0.00');
  const cursor = 1;
  return (
    <TextInput
      ref={ref}
      autoFocus
      onKeyPress={() => { /* empty */ }}
      selection={{start: cursor, end: cursor}}
      value={state}
      keyboardType="numeric"
    />
  );
};

There is no way to prevent default behavior, event.preventDefault() also doesn't work. The input is always updated even if the component is controlled.

@cihangir-mercan
Copy link

The issue still persists on both IOS and Android

I try to limit decimal points to 8 length. The user sees 9th character shortly before removal. It causes flickery.

https://snack.expo.dev/N5IcDRFWc

@fabOnReact
Copy link
Contributor

Do you still experience this issue?

I have four years of experience maintaining facebook/react-native and I specialize in the Text and TextInput components. I currently have 58 facebook/react-native PRs.

If you still experience this issue, I will prepare a patched release with the fix.

Thanks a lot

@DanielO199
Copy link

Yes, this bug still occurs. Please take care of it

@yuki384
Copy link

yuki384 commented Jan 21, 2024

@fabOnReact
Yes, the issue still persists (on iOS). If you could tackle this, it would be incredibly appreciated!

@mkhoussid
Copy link

mkhoussid commented Jan 21, 2024

Do you still experience this issue?

I have four years of experience maintaining facebook/react-native and I specialize in the Text and TextInput components. I currently have 58 facebook/react-native PRs.

If you still experience this issue, I will prepare a patched release with the fix.

Thanks a lot

Kind of a hacky work around (and doesn't always work quite as expected):

const onChange = (e: string) => {
    if (isValidInput(e)) {
        textInputRef.current?.setNativeProps({ text: e });
    }
}

@yuki384
Copy link

yuki384 commented Jan 22, 2024

Kind of a hacky work around (and doesn't always work quite as expected):

const onChange = (e: string) => {
    if (isValidInput(e)) {
        textInputRef.current?.setNativeProps({ text: e });
    }
}

Thank you! I already tried that, but It's still flickering on device... It works fine on Simulator.
Incidentally, I would like to prevent type new lines.

@mkhoussid
Copy link

mkhoussid commented Jan 22, 2024

Kind of a hacky work around (and doesn't always work quite as expected):

const onChange = (e: string) => {
    if (isValidInput(e)) {
        textInputRef.current?.setNativeProps({ text: e });
    }
}

Thank you! I already tried that, but It's still flickering on device... It works fine on Simulator. Incidentally, I would like to prevent type new lines.

Can you try something like this?

. . .
const previousValueRef = React.useRef('');
. . .
const onChange = (e: string) => {
    if (textInputRef.current) {
        const textInput = textInputRef.current;
        textInput.setNativeProps({ text: previousValueRef.current });
        if (isValidInput(e)) {
            textInput.setNativeProps({ text: e });
            previousValueRef.current = e;
        }
    }
}
. . .

If you need to accept only integers and know your max length, there was a suggestion from this SO post:

<TextInput
  maxLength={MAX_LENGTH}
  keyboardType="numeric"
  value={value}
  onChangeText={(e) => {
    if (/^\d+$/.test(e)) setValue(e);
  }}
  placeholder="Type only numbers..."
/>

@fabOnReact
Copy link
Contributor

This PR is included in the react-native-improved library:

react-native-improved

  • Supports ONLY react-native 0.73.
  • Supports only old architechture (new architechture is WIP)

Set-up

In package.json

 "scripts": {
+  "postinstall": "yarn run react-native-patch"
 }

Then

npm

npm install react-native-improved --save-dev

yarn v1

yarn add react-native-improved --dev

@yuki384
Copy link

yuki384 commented Feb 5, 2024

Can you try something like this?

Thank you!
This did not work out well either. However, it might have been due to me including a Text component as a child of TextInput to finely control the style :

<TextInput ... >
  <Text>Some</Text>
  <Text style={{ ... }}>String</Text>
</TextInput>

For my case, I've decided to use a custom Native Module, but I'll give your solution a try another time.

@react-native-bot
Copy link
Collaborator

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@react-native-bot react-native-bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Aug 3, 2024
@jcubic
Copy link

jcubic commented Aug 3, 2024

A PR with a fix was created a year ago, I don't know what else I can do.

@cortinico
Copy link
Contributor

A PR with a fix was created a year ago, I don't know what else I can do.

What's the PR?

@jcubic
Copy link

jcubic commented Aug 3, 2024

It was linked:

#38155

@cortinico
Copy link
Contributor

It was linked:

#38155

That's an issue, not a PR

@jcubic
Copy link

jcubic commented Aug 3, 2024

That's an issue, not a PR

Ah sorry my mistake, I thought it was the issue where I created a PR for.

@react-native-bot react-native-bot removed the Stale There has been a lack of activity on this issue and it may be closed soon. label Aug 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Component: TextInput Related to the TextInput component. Platform: Android Android applications. Priority: Mid
Projects
None yet