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

Duplicated letters when autoCapitalize="characters" on android #11068

Closed
joncrocks opened this issue Nov 22, 2016 · 121 comments · Fixed by fabOnReact/react-native-improved#16
Closed
Assignees
Labels
Bug Component: TextInput Related to the TextInput component. Platform: Android Android applications. Ran Commands One of our bots successfully processed a command. Resolution: PR Submitted A pull request with a fix has been provided.

Comments

@joncrocks
Copy link

joncrocks commented Nov 22, 2016

Description

When trying to implement an upper case only input, adding autoCapitalize="characters" seems to work on Android by setting the keyboard to be upper case by default. One can hit the shift key and then type a lower case letter.

To ensure that we only let the user enter (and see) upper case letters, I thought that I might be able to handle it by ensuring that when we update the react state of the component we capture the text in upper case.

By using a toUpperCase on top of what is a pretty standard state update cycle (i.e. a very similar update method ala the examples at https://facebook.github.io/react-native/releases/next/docs/textinput.html ), this saves the input into the state, uppercased, ready for the next render cycle. (I'm not concerned about the dangers of toUpperCase at this point.)

Unfortunately, the behaviour is a bit strange when you start typing both upper and lowercase letters, where you start getting repeated letters, e.g. if I type AbC, I will end up with ABABC, AbcdE I get ABABCDABABCDE.

Reproduction

I created an example app here: https://rnplay.org/apps/t-gBOA
Note that the behaviour seems fine on the iOS simulator, but is 'wrong' on the android simulator.

or see the component below:

import React, {Component} from "react";
import {View, TextInput} from "react-native";

export class UpperCaseTextEntry extends Component {
    
    constructor() {
        super();
        this.state = {
            text: ""
        }
    }
    
    upperCaseIt(text) {
        var textUpperCase = text.toUpperCase();

        this.setState({text: textUpperCase});
    }
    
    render() {
        var text = this.state.text;
        return (
            <View>
                <TextInput value={text} autoCapitalize="characters"
                           onChangeText={(text) => {this.upperCaseIt(text)}}
                />
            </View>
        )
        
    }
}

Solution

I suspect that there's something going awry with the syncing of state between the react state and the state of the underlying components, quite possibly some case-ignoring checks are being in some places, but not others.

Additional Information

  • React Native version: 0.35.0
  • Platform: Android
  • Operating System: Windows (building/testing on windows)

I've also noted during that it's more than possible to fire multiple onChangeTexts before a render is triggered, which could lead to unexpected app behaviour. This could be expected unexpected behaviour though :-)

@joncrocks joncrocks changed the title Duplicated letters when modifying state Duplicated letters when autoCapitalize="characters" on android Nov 22, 2016
@kenmasters
Copy link

Are there any updates on these issue already? , I am also experiencing this.

@magneticz
Copy link

having same issue with lowercasing, every time a capital case character is entered, the text that is passed to onChangeText function contains exact duplicate of existing text plus last entered character. it happens only on android devices, on ios it works fine

@rob-64
Copy link

rob-64 commented Feb 4, 2017

Experiencing same with lowercase.

RN: 0.39.2
Phone: samsung galaxy S7

@klaasman
Copy link

klaasman commented Mar 3, 2017

Any news on this issue?

@afilp
Copy link

afilp commented Mar 18, 2017

I have exactly the same problem and cannot release without a fix...

RN: 0.42.3
Phone: Galaxy s6 edge

Is anybody working on this? Thanks.

@MSchmidt
Copy link

Same issue.

RN: 0.44.3
Phone: Galaxy S8

Cannot reproduce this in emulator, so maybe something Samsung specific?

@skabbes
Copy link

skabbes commented Jul 6, 2017

Since the rnplay link is broken, I have recreated it on Expo Snack here:

https://snack.expo.io/S1ksBZsEW

Samsung Galaxy S8 is breaking for me.

@andreiAndrade
Copy link

When I disable the "Predictive Text" at "Language and Input > Samsung keyboard settings" the issue is solved, but how to solve that programmatically? :/

@JumalDB
Copy link

JumalDB commented Aug 14, 2017

Same issue

RN: 0.44.0
Phone: Samsung Galaxy S7 Edge

Any fix?

@sonikro
Copy link

sonikro commented Aug 16, 2017

Same issue.
Phone: Samsung Galaxy S7 / Moto Z2 Play
Keyboards: Samsung Keyboard (with predict), Swiftkey Keyboard and Google Keyboard
RN: react-native@0.47.1

@renatoagds
Copy link

renatoagds commented Aug 17, 2017

Same issue here.
Phone: OnePlus 5
Keyboard: Fleksy@8.4.6
RN: 0.46.2

@ollyde
Copy link

ollyde commented Aug 29, 2017

Same issue here.
Android 7.0 emulator, google pixel hardware selected.
RN: 0.46.2

@vko-online
Copy link

same for Samsung Galaxy
@facebook ?

@danparker276
Copy link

What's the work around? Just don't use autocapitalize and your own?

@Pianist038801
Copy link

Experienced same issue. I thought that in Android onChangeText() is triggered multiple times or so. But after checking, it's not what I thought it would be. Totally no hint of solving the issues at this moment.

@conor909
Copy link

conor909 commented Sep 27, 2017

Same issue here. It seems to be on any android devices with swipe / SwiftKey keyboards turned on.
I think this issue should be escalated as these keyboards come pre-installed on a lot of devices.

@williamoliveira
Copy link

williamoliveira commented Oct 3, 2017

Same issue, I was getting paranoid cause I couldn't find the reason
For me it doesn't even matter if you set autoCapitalize or not

Do anyone know a workaround?

"react-native": "^0.46.1",
Xiaomi Redmi Note 4
Google Keyboard

@ankitpopli1891
Copy link

I stumbled upon the same issue. Found out the autoCapitalize was not working due to fontFamily specified in style prop.

Seems like it was failing silently. Will update here, if I find anything else.

@avynarchuk
Copy link

Try to set autoCorrect to false and check if problem exist yet.

@MSchmidt
Copy link

It does. autoCorrect has nothing to do with it.

@M1chaelTran
Copy link

M1chaelTran commented Oct 27, 2017

I have the same issue but found a workaround for my current needs.

The issue can't be re-produce via the simulator using your keyboard.
However, if you use the onscreen keyboard, then you can see the text is duplicated.

For my case, since the user can't use a 'keyboard' except for the on-screen keyboard provided.
In adding, it only became an issue when I use the text.toUpperCase()
Thus, setting the text state in lower case and autoCapitalize="characters" seems to work fine for my case.
Of course, if you do not want the user to ever be able to enter lowercase, then this is an issue.

PS: I specified a fontFamily in style and autoCapitalize still work as expected

@john1jan
Copy link

john1jan commented Nov 6, 2017

I am facing the same issue..

@john1jan
Copy link

john1jan commented Nov 6, 2017

Any update on this issue . I just tried to use toUpperCase on my textinput. Its dupllicates the characters.

@john1jan
Copy link

john1jan commented Nov 6, 2017

ezgif com-optimize

@tastafur
Copy link

tastafur commented Dec 12, 2017

Has anyone found a solution for this bug?, I have the same problem in all samsung that have the predictive language activated

@MedinaGitHub
Copy link

the problem is toUpperCase() , i use <TextInput autoCapitalize='characters' ...

@MSchmidt
Copy link

MSchmidt commented Feb 8, 2018

@MedinaGitHub of course the problem is the use of toUpperCase(). We use this to enforce uppercase and this causes this bug on Samsung devices.

@williamoliveira
Copy link

Not only Samsung

@lucassouza16
Copy link

um colega levantou esta questão hoje em um grupo do FB... o bug ainda existe?

Yes

@Fortidude
Copy link

Hello from 2023 :D

@batazo
Copy link

batazo commented Mar 7, 2023

In 2027 I will be fixed

@lunaleaps lunaleaps assigned lunaleaps and unassigned JoshuaGross Mar 15, 2023
@ace-builds
Copy link

Any solution to this yet?

@hugoassisj
Copy link

Any real solution for this?

@efstathiosntonas
Copy link

Hey folks, there's an open PR for this

@NazarB99
Copy link

NazarB99 commented May 2, 2023

Any real solution for this?

found only this solution, add these props to your TextInput

autoCapitalize="none"
secureTextEntry={true}
keyboardType={"visible-password"}

@ybf970928
Copy link

Any real solution for this?

found only this solution, add these props to your TextInput

autoCapitalize="none"
secureTextEntry={true}
keyboardType={"visible-password"}

yes, work on 0.64

@lunaleaps
Copy link
Contributor

lunaleaps commented Jul 13, 2023

I'm trying to verify this issue on 0.72 but I can't reproduce with this functional component:

function UpperCaseTextEntry() {
  const [text, setText] = React.useState('');
  return (
    <View>
      <TextInput
        value={text}
        autoCapitalize="characters"
        onChangeText={t => {
          const uppercase = t.toUpperCase();
          setText(uppercase);
        }}
      />
    </View>
  );
}

Same with this version of the original repro

class UpperCaseTextEntry extends Component<{}, {text: string}> {
  constructor(props: {}) {
    super(props);
    this.state = {
      text: '',
    };
  }

  toUpperCase(text: string) {
    this.setState({text: text.toUpperCase()});
  } 

  render() {
    return (
      <View>
        <Text>My Attempt 2</Text>
        <TextInput
          value={this.state.text}
          autoCapitalize="characters"
          onChangeText={t => this.toUpperCase(t)}
        />
      </View>
    );
  }
}

Is anyone able to reproduce with either of the above?

screencap-2023-07-13T021531.477Z.mp4

juniorklawa pushed a commit to juniorklawa/react-native that referenced this issue Jul 20, 2023
…owercase or uppercase in TextInput (facebook#35929)

Summary:
These changes are intended to resolve facebook#11068.

## Changelog:

[Android] [Fixed] - Fix letters duplication when using autoCapitalize

Pull Request resolved: facebook#35929

Test Plan:
I took the `RewriteExample` from `TextInputSharedExamples.js` duplicated it, updated the labels, attached to the same screen. Modified its `onChangeText` function, from `text = text.replace(/ /g, '_');` to `text = text.toLowerCase();` then tested via rn-tester as shown in the video:
- No duplicate characters
- Characters are updated to be lowercase
- Long pressing delete properly deletes, doesn’t stop after deleting one character
- Suggestions (selected from keyboard) work and are updated to lowercase when it becomes part of the input text
- Moving the cursor and typing works, cursor position is kept as it should
- Moving the cursor and deleting works
- Selection portion and deleting it works, cursor position is kept as it should

https://user-images.githubusercontent.com/14225329/213890296-2f194e21-2cf9-493f-a516-5e0212ed070e.mp4

Note: I have tested manually with 0.67.4, because later versions failed on my machine with cmake and argument errors when building the rn-tester from Android Studio to any device.
Help regarding that would be appreciated.

## Possible Future Improvements

As it can be seen the video, the letter duplication is resolved, however since the lowercase modification happens on the Javascript side, it takes a couple milliseconds and the Uppercase can still be shown momentarily while typing.

## Technical details, why the solution works

I've debugged a simple AppCompatEditText with calling the same `getText().replace` in `doAfterTextChanged` with a bit of delay and noticed a difference to the `ReactEditText`.

The ReactEditText removes the `android.view.inputmethod.ComposingText` Span in `manageSpans` before calling replace (ComposingText is `Spanned.SPAN_EXCLUSIVE_EXCLUSIVE`).
That `ComposingText` Span is used in `android.view.inputmethod.BaseInputConnection` `private replaceText` to find from what point the text should be replaced from when applying suggestions or typing new letters. Without that Span, it defaults to the selection position, which is usually the end of the text causing duplication of the old "word".

**In simple terms, while typing with suggestions on the keyboard, each new letter is handled similarly as clicking a suggestion would be, aka replacing the current "word" with the new "word". (let's say "Ar" word with "Are" word)**

Another way to describe it:
While typing with suggestions, with the ComposingText Span the TextView keeps track of what word completions are suggested for on the keyboard UI. When receiving a new letter input, it replaces the current "word" with a new "word", and without the Span, it replaces nothing at the end (selection point) with the new word which results in character duplication.

It also seems to make sense then why without suggestions (like password-visible and secureTextEntry) the issue hasn't occurred.

### Examples

How the issue happened:
> - User types: A (ComposingText Span becomes (0,1), composingWord: "A")
> - Javascript replaced A with a, ComposingText Span was removed from getText()
> - User types a new character: r (ComposingText, defaulting to selection, from selection, empty string is replaced with word "Ar")
> => Complete text: aAr => letter duplication.

How it works with the changes applied:
> - User types: A (ComposingText Span becomes (0,1), composingWord: "A")
> - Javascript replaces A with a, (ComposingText Span (0,1) is re-set after replace)
> - User types a new character: r (ComposingText (0,1), "a" word is replaced with word "Ar". ComposingText becomes (0,2) "Ar")
> - Javascript replaced Ar with ar, (ComposingText Span (0,2) is re-set after replace)
> => Complete text: ar => no letter duplication
> - User selects suggestion "Are" (ComposingText Span (0,2) "ar" is replaced with new word and space "Are ")
> - CompleteText: "Are "
> - Javascript replaces "Are " with "are " (ComposingText Span doesn't exist, no string after space " ")

Note: the Editable.replace also removes the ComposingText, if it doesn't cover the whole text, that's why we need to re-setSpan them even if we wouldn't remove them in `manageSpans`.

## Note

This is my first attempt to contribute so if I missed something or messed up, please point out and I will try to adapt.

Reviewed By: NickGerleman

Differential Revision: D47243817

Pulled By: lunaleaps

fbshipit-source-id: 5f3551d17466f2c7cd1aa89ffb09af50e065b52e
@seba9999
Copy link

seba9999 commented Sep 1, 2023

I'm still experiencing this 😢

I mean I still have the duplicated letters... Not the short "blink" of an uppercase as @fknives mentionned HERE

const [text, setText] = React.useState('');
<TextInput
  value={text}
  autoCapitalize="characters"
  onChangeText={t => {
    const uppercase = t.toUpperCase();
    setText(uppercase);
  }}
/>

It -only- happens when I use the "shift" key to toggle back to LowerCase on the keyboard
( Because the autoCapitalize="characters" force it to be uppercased by default.)

I mean that the duplication happends only when the letter typed is a lowercase one ... ( thus .toUpperCase() does something )

But does this code shouldn't work without the props autoCapitalize at all ? Which is not the case !

Running on :

  • expo SDK 49 which should be RN 0.72.4 then

@fknives
Copy link
Contributor

fknives commented Sep 1, 2023

@seba9999 Sadly, I made a mistake in my pull request and it had to be reverted.
I created a new one (which you linked) which should resolve the issue without the previous mistakes.

However, that is still waiting for review and merge (understandably since I made a pretty big mistake int he first one). So the issue is not resolved until that is merged. (Or until someone else's solution is merged before that)

@unyo
Copy link

unyo commented Sep 30, 2023

@lunaleaps I've encountered this issue on 0.64 but only on a Samsung device (via QA), and with "predictive text" enabled. On my Pixel 2XL, it runs a different keyboard and there isn't a way to turn on predictive text afaik, so there isn't a way to repro (looking at your video, it looks more like my Pixel keyboard rather than the Samsung keyboard). Turning off predictive text seems to fix the issue, as per:

When I disable the "Predictive Text" at "Language and Input > Samsung keyboard settings" the issue is solved, but how to solve that programmatically? :/

Has anyone found a solution for this bug?, I have the same problem in all samsung that have the predictive language activated

Could you try repro'ing using a Samsung device?

I haven't read enough of the codebase to contribute yet, but so far the behavior looks most similar to this breakdown of the situation: #11068 (comment)

EDIT: Apparently samsung has an emulator online that has the Samsung keyboard (android emulator uses AOSP keyboard even on Samsung Tab S3) https://developer.samsung.com/remotetestlab/webclient/

I used this to test my stuff, I think maybe it either has something to do with the style textTransform: 'uppercase' (which transforms the actual value of the TextInput rather than it being a display transformation like in the browser, or it has something to do with doing .toUpperCase() inside onChangeText (I was able to transform the text within value={text.toUpperCase()}). I don't have a Samsung device and I have limited credits so I'm not able to look into this further.

Solution:

onChangeText = (text) => {
  this.setState({ text })
}
render() {
  return (
    <TextInput
      onChangeText={this.setChangeText}
      autoCapitalize="characters" // this doesn't capitalize the text, but in iOS and Samsung it sets the keyboard to caps mode. in Pixel it doesn't, so we still need to uppercase it manually. its not necessary but I keep it in here because I like not having a flash of lowercase
      value={(text || '').toUpperCase()}
      maxLength={6} // you can probably truncate in render too using .slice(0, 6), but idk, this is what I use
    />
  )
}

@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

@efstathiosntonas
Copy link

@fabriziobertoglio1987 are all these PRs included in the react-native-improved repo? 🤔

@fabOnReact
Copy link
Contributor

fabOnReact commented Jan 17, 2024

I added PR #29070 to my todolist.

@fabriziobertoglio1987 are all these PRs included in the react-native-improved repo? 🤔

I will work on this issue and publish the solution. I will keep the community updated on the progress. Thanks for the support.

@efstathiosntonas
Copy link

efstathiosntonas commented Jan 17, 2024

great, thanks Fabrizio. There are soooo many open prs around TextXX. I have pinged meta team in a pre-release discussion about all of them but they decided to not include anything 🤷‍♂️ at that time. I’ll find the list and I’ll open an issue on react-native-improved.

@fabOnReact
Copy link
Contributor

fabOnReact commented Jan 17, 2024

great, thanks Fabrizio. There are soooo many open prs around TextXX. I have pinged meta team in a pre-release discussion about all of them but they decided to not include anything 🤷‍♂️ at that time. I’ll find the list and I’ll open an issue on react-native-improved.

Thanks @efstathiosntonas.

  1. To be included in the facebook/react-native release, the PR needs to be merged in main branch. We need a commit hash
  2. Meta uses react-native in Facebook Marketplace and other apps. Facebook Marketplace uses the main branch from facebook/react-native
  3. For this reason, it is hard to merge the PR in main branch. Meta react-native team also has limited resources to review and merge every PR.

Using inheritance, I can apply patches to react-native Text or TextInput component without building from source.

For example, I applied PR #41770 (Issue #39722) to react-native-improved using inheritance with ReactTextView, ReactTextViewManager and ReactTextShadowNode.

The project is a now proof of concept. I plan to complete the project in 1-2 months, for this reason I'm currently preparing a list of tasks with different priorities.

@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

@SamGeodynamics
Copy link

SamGeodynamics commented Oct 11, 2024

for people still having this issue on android, see if you disabled the keyboard setting "auto-capitalize first letter", found this out when 2 stock phones worked but my personal device didn't auto-capitalize, drove me nuts :-)

if you enable it again (the default setting), the option started working on my device. still not entirely a fix but at least I know where it bugged out.

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