React-native: TextInput cursor jump to right end when the input is empty (Android only)

Created on 30 Apr 2020  路  17Comments  路  Source: facebook/react-native

Please provide all the information requested. Issues that do not follow this format are likely to stall.

Description

TextInput that has the placeholder set, and styles with textAlign: "center" and manages the value with useState hook, makes the cursor jump to the end of the input field when we clear the contents with backspace.

import React, { useState } from "react";
import { StyleSheet, Text, View, TextInput } from "react-native";

export default function App() {
  const [state, setState] = useState("");

  return (
    <View>
      <Text>Open up App.tsx to start working on your app!</Text>
      <TextInput
        style={styles.input}
        placeholder="My placeholder"
        value={state}
        onChangeText={setState}
      ></TextInput>
    </View>
  );
}

const styles = StyleSheet.create({
  input: {
    marginTop: 50,
    backgroundColor: "yellow",
    textAlign: "center",
    width: "100%",
  },
});

Related StackOverflow question: https://stackoverflow.com/questions/60276121/textinput-cursor-jump-to-right-end-when-the-input-is-empty

React Native version:

Run react-native info in your terminal and copy the results here.

System:
OS: macOS Mojave 10.14.6
CPU: (4) x64 Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
Memory: 39.64 MB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 8.11.4 - ~/.nvm/versions/node/v8.11.4/bin/node
Yarn: 1.9.4 - /usr/local/bin/yarn
npm: 6.14.1 - ~/.nvm/versions/node/v8.11.4/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.9.1 - /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms: iOS 13.1, DriverKit 19.0, macOS 10.15, tvOS 13.0, watchOS 6.0
Android SDK:
API Levels: 28, 29
Build Tools: 28.0.3, 29.0.2
System Images: android-28 | Intel x86 Atom_64, android-28 | Google APIs Intel x86 Atom_64, android-28 | Google Play
Intel x86 Atom, android-29 | Google APIs Intel x86 Atom
Android NDK: Not Found
IDEs:
Android Studio: 3.5 AI-191.8026.42.35.6010548
Xcode: 11.1/11A1027 - /usr/bin/xcodebuild
Languages:
Java: 1.8.0_241 - /usr/bin/javac
Python: 2.7.16 - /usr/bin/python
npmPackages:
@react-native-community/cli: Not Found
react: ~16.9.0 => 16.9.0
react-native: ~0.62 => 0.62.2
npmGlobalPackages:
react-native: Not Found

Steps To Reproduce

Provide a detailed list of steps that reproduce the issue.

  1. Create an empty RN project
  2. Add the TextInput with the styles in the code above to the App.tsx file
  3. Run the app on a Android device
  4. Click the text input (Cursor should be at the center)
  5. Type some text (Cursor should still be placed correctly)
  6. Hit backspace to clear the input (Now when the input is completely cleared the cursor jumps all the way to the right)

Some times, when listening to changes in dev mode to update the app, the faulty behaviour is not noticed at first. When that happens, I comment out the placeholder prop and save for the app on my android emulator to refresh. Then I uncomment it again, and save to refresh one more time. With this I am able to reproduce the problem 100% of the time.

Expected Results

When the style textAlign: "center" is set, the cursor should stay in the center after you clear the input.

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

Please select the Android platform on snack to see the problem
https://snack.expo.io/QuGin01cF

example

TextInput Android

Most helpful comment

All 17 comments

This might also relate to https://github.com/facebook/react-native/issues/28936, and how it affects only some devices

Adding/Removing the placeholder triggers the problem. Tested on latest master

<TextInput 
      placeholder="myPlaceholderProp" />

The TextInput placeholder prop is set with the following method. Commenting setHint seems to remove the placeholder and the issue.

https://github.com/facebook/react-native/blob/1a77943e8bef97e7bee903c7bfcf32c56fb8fab8/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java#L407-L410

Similar issues are experienced using the placeholder setHint with EditText android class. Probably the workaround used in react-native is similar to the one proposed in the stackoverflow answer (using setGravity(Gravity.CENTER)).

Over-riding measure
1) set the placeholder editText.setHint(getPlaceholder());
2) measure() and then call YogaMeasureOutput.make().
The result is that measure() returns a different value.

https://github.com/facebook/react-native/blob/1a77943e8bef97e7bee903c7bfcf32c56fb8fab8/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java#L103-L135

This has probably an effect on the layout but commenting the line editText.setHint does not remove the bug... so I believe the bug is connected to the layout function responsibile for setting textAlign: "center"

Maybe the issue is connected to using setGravity

https://github.com/facebook/react-native/blob/1a77943e8bef97e7bee903c7bfcf32c56fb8fab8/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java#L564-L570

https://github.com/facebook/react-native/blob/9263eb5d3864a42925b699343db2c09cc8934ed0/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java#L618-L627

I commented all the textAlign and setHorizontalGravity logic, the problem would obviously not reproduce until I set in ReactEditText

    @Override
    public void afterTextChanged(Editable s) {
      if (!mIsSettingTextFromJS && mListeners != null) {
        for (TextWatcher listener : mListeners) {
          listener.afterTextChanged(s);
        }
      }
      setGravity(Gravity.CENTER);
    }

so the cause of this issue is using setGravity(Gravity.CENTER).

I'll keep testing the rest of the logic and hopefully I find the solution. Thanks

What about how it affects webview's? For me, users are having issues with a react native scripted website through the webview. @fabriziobertoglio1987

@TheAlienDrew if you can open an issue with reproducible example, I will work on it. If I have a solution, I ll write a pull request. Thanks

@fabriziobertoglio1987 thanks for taking a look at this!

I found a possible solution, the issue is caused from calling the placeholder setter before calling the textAlign setter.

Adding to the below method

https://github.com/facebook/react-native/blob/c8fed9e3858876c4e9cfe452a9f51c9241f82aad/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java#L848-L855

setGravity(Gravity.CENTER_HORIZONTAL);
setHint("this hint does not break");

Does not cause the bug.

While if we set the text via the javascript setter prop placeholder

https://github.com/facebook/react-native/blob/c8fed9e3858876c4e9cfe452a9f51c9241f82aad/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java#L407-L410

setHint is called before my setGravity(Gravity.CENTER_HORIZONTAL) and it breaks

I already experienced this with other issues in react-native, so I believe that I can publish a pull request to fix this within end of this week.

Thanks a lot :smiley:

It does not reproduce when I call setHint("my test hint") because there are 2 methods triggered from the <TextInput placeholder /> prop (using setHint and setGravity(Gravity.CENTER) manually skips the below logic):

1) ReactTextInputManager method setPlaceholder responsible to set the hint (or placeholder) text

https://github.com/facebook/react-native/blob/fd97e955f8fa6b30603ce4d3757a317222f47f81/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java#L407-L410

2) ReactTextInputShadowNode method setPlaceholder responsible to trigger the re-calculation of measure using markUpdated().

https://github.com/facebook/react-native/blob/fd97e955f8fa6b30603ce4d3757a317222f47f81/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java#L186-L190

POINT 2 is required as the placeholder text is used to compute the dimensions in measure callback, by creating a dummy editText and then computing the dimensions.

https://github.com/facebook/react-native/blob/fd97e955f8fa6b30603ce4d3757a317222f47f81/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java#L128-L135

Additionally style: { textAligh: "center" } trigger the following method:

https://github.com/facebook/react-native/blob/1a77943e8bef97e7bee903c7bfcf32c56fb8fab8/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java#L568-L570

I believe setGravity will work only if measure compute the dimensions after the placeholder was calculated. Removing and Readding the placeholder will break the logic maybe by calling setGravity before measure...

related: https://stackoverflow.com/questions/38934776/cannot-resize-linear-layout-children-in-android-react-native-module

possible solution inside ReactTextInputManager. Need to do extensive testing.

@ReactProp(name = "placeholder")
  public void setPlaceholder(ReactEditText view, @Nullable String placeholder) {
    String text = placeholder == null ? "" : placeholder;
    view.setHint(text);
  }

the problems seems to be connected to the use of the Stringish type for the placeholder field, which allows placeholder to default to null instead of empty string.

https://github.com/facebook/react-native/blob/5cde6c5e7d440e62d21ef858d77ed185df8a2da0/Libraries/Components/TextInput/TextInput.js#L605

https://github.com/facebook/react-native/blob/5cde6c5e7d440e62d21ef858d77ed185df8a2da0/flow/Stringish.js#L14

Probably value of null causes problems when placeholder value is removed and measure is triggered to re-compute the content size..

The issue is caused by the useage of Maybe Types like ? which allow to pass null as value.

Seems like the one above is the easiest solution to implement as there are other logic built on top of this placeholder prop that could use the value of null... additionally change in the type would break old applications .. which may pass placeholder={null}

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.

I believe it still requires attention. @fabriziobertoglio1987 has this PR that seems to fix the problem. It as a small 2 liner, but for some reason it was not merged yet. So if anyone could take a look at that it would be super cool.

I'm facing the same issue and it's quite annoying, please help to verify and merge @fabriziobertoglio1987 's PR :(

facing the same issue, my phone model is Realme 6 pro

Hi @fabriziobertoglio1987, if I wanna try your solution, all I need to do is change the logic in the /node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java right?

But after I add the changes

    String text = placeholder == null ? "" : placeholder;
    view.setHint(text);

And run npx react-native run-android, the issue isn't resolved, could you please help me to clarify this

@nenjamin2405 bro did you find a Solution
upper solution is not working for me too

@nenjamin2405 @Haris-Shahid sorry but currently I don't have much free time to work on this. Did you build from source? For example if you log something like Log.w("TESTING::ReactTextInputManager", "my log message"), do you see the logs with adb logcat? Because I don't make modifications to node-modules to build this fix but I follow the instructions on building from source on Android https://github.com/facebook/react-native/wiki/Building-from-source

My point is, if it does not work, then try to log something in the Android Source with Log.w (not a console.log) to make sure your change is included, then if you can log with Log.w, try to understand why this fix does not work anymore..

Because as you can see in my pull request https://github.com/facebook/react-native/pull/28995 I included videos showing that my change fixes this issue.

So I believe your fix does not work, because you are changing node_modules ReactAndroid files, but you need to build the .aar android package that ReactNative will use.

https://github.com/facebook/react-native/wiki/Building-from-source#publish-your-own-version-of-react-native

You will need to build React Native from source if you want to work on a new feature/bug fix, try out the latest features which are not released yet, or maintain your own fork with patches that cannot be merged to the core.

Thanks. Have a good weekend

Was this page helpful?
0 / 5 - 0 ratings