react-native: Can't modify text in TextInput onChangeText callback on Android
🐛 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: https://github.com/facebook/react-native/issues/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® Core™ 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
About this issue
- Original URL
- State: open
- Created 5 years ago
- Reactions: 21
- Comments: 65 (11 by maintainers)
Hello ReactNative Developers! 😃
I just prepared Pull Request https://github.com/facebook/react-native/pull/29070 that seems to solve this issue
PREVIEWS OF THE BUGFIX
You can contact me by email at fabrizio.developer@gmail.com
Please head over to the Pull Request https://github.com/facebook/react-native/pull/29070 and thumbs up if you like it. If you want to get this fix you can follow the instructions for building ReactAndroid or checkout my video introduction on forking react-native (patch-package does not work).
If you don’t like the pr please feel free to leave a code review or comment, I’ll be happy to add improvements and changes.
Thanks a lot 🙏 ☮️ 🏖️
I am not sure if this would also work on IOS, but it solved the problem for me on Android. The answer is quite simple: NEVER use value when rendering a
TextInput
. UsedefaultValue
instead.If this turns out to really fix the issue for every platform, maybe we could just issue a warning every time someone uses
value
as a prop toTextInput
.UPDATE: I’ve tried this in iOS, and it also works.
Still an issue.
defaultValue
doesn’t solve the problem. If you want to mask user inputs and show user masked inputs, still have to use value prop.Still seeing this over here as well.
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).
Still present in “v0.63.2” which was released couple days ago. @hramos
You could mess around with a Math.random() key for the input field. This blurs the focus after each keystroke though, so you’d have to write code to re-focus and move the cursor to the end. Personally, I no longer modify the input at all on Android, and instead just do the all-caps (or whatever) on the back end before saving to the DB. Not the best user experience. It’s too bad the RN team seems uninterested in fixing pretty major issues like this one, and instead seems to spend their time making breaking changes to APIs, moving things arbitrarily out of the core APIs, etc.
same here on version “v0.63.2”
Experience the same bug right now. Basically there is no way to implement something as simple as masked input.
Here, before it becomes stale.
https://snack.expo.io/@anchi/8017c6
PS: run this on a real device.
This issue still persists. This is a pretty serious issue for inputting text and there is no special workaround except make a specific event triggering component (like a button) to ensure the textchange happens correctly. Using onEndEditing and oneSubmitHandler also doesn’t do anything. Please correct this soon.
My report of this bug
Summary
When attempting to ignore certain characters by controlling the state value that’s passed to the TextInput, the TextInput’s natively held value get’s out of sync. This makes it impossible to enforce a particular text pattern at the time of a user entering the text. This exact issue was occurring before React Native 57.1 on iOS, as noted here in this now closed issue #18874 .
React Native version: 0.59.8(based on Expo SDK 34)
Steps To Reproduce
onChangeText
event handler that will selectively choose to not update the state value(which control’s the TextInput’s natively stored value). For example, let’s say uppercase letters like ‘A’, ‘B’, ‘C’, etc…Describe what you expected to happen:
Snack, code example, screenshot, or link to a repository
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
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.
The same issue on Android in Emulator. It works fine for the Expo playground so don’t use it to test.
Simple reproduction:
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.I look again into this when I have more free time. I need to update my PR with the improvements included in https://github.com/facebook/react-native/pull/33468. For this reason https://github.com/facebook/react-native/pull/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.@fabriziobertoglio1987 You are awesome! Thank you so much.
Same here. Experiencing this with
react-native-masked-input
.@lgenzelis, thanks! So what
value
is good for again?Can you try something like this?
If you need to accept only integers and know your max length, there was a suggestion from this SO post:
Kind of a hacky work around (and doesn’t always work quite as expected):
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
What I got to work on Android while @fabriziobertoglio1987’s PR is open. I want text input to be uppercased.
autoCapitalize='characters'
works on iOS but there needs to be some additional properties so android doesn’t jumble keystrokes@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.
Shouting into the wind! Hope the next person can use this as a basis for reporting the issue.