React-native: Crash on email detection in TextInput on Xiaomi devices running android 10

Created on 12 Nov 2019  ·  64Comments  ·  Source: facebook/react-native

Xiaomi devices running android 10 show a little popup with the Text Frequent email when they detect a valid email address in the text input currently selected. This feature causes some crashes on my react native app. Sadly I can't reproduce in on an empty project, but it always happens on some inputs in my app. I have not been able to determine the difference between the textinputs that causes the problem and the textinput that does not cause any crash.

React Native version: 0.61.2

Steps To Reproduce

  1. Select a textinput
  2. digit a valid email address (such as [email protected]). As soon as I type the last c (creating a valid email format), the app crashed

The error:

java.lang.NullPointerException · Attempt to invoke direct method 'void android.widget.Editor$SelectionModifierCursorController.initDrawables()' on a null object reference

The full stack trace:

java.lang.NullPointerException: Attempt to invoke direct method 'void android.widget.Editor$SelectionModifierCursorController.initDrawables()' on a null object reference
        at android.widget.Editor$SelectionModifierCursorController.access$300(Editor.java:6696)
        at android.widget.Editor.getEmailPopupWindow(Editor.java:1469)
        at android.widget.Editor.showEmailPopupWindow(Editor.java:1477)
        at android.widget.Editor.handleEmailPopup(Editor.java:1456)
        at android.widget.Editor.updateCursorPosition(Editor.java:2099)
        at android.widget.TextView.getUpdatedHighlightPath(TextView.java:7813)
        at android.widget.TextView.onDraw(TextView.java:7998)
        at android.view.View.draw(View.java:21472)
        at android.view.View.updateDisplayListIfDirty(View.java:20349)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4396)
2019-11-12 17:19:57.876 20111-20111/com.yourvoice.ccApp.dev E/AndroidRuntime:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4369)
        at android.view.View.updateDisplayListIfDirty(View.java:20309)
        at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:575)
        at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:581)
        at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:654)
        at android.view.ViewRootImpl.draw(ViewRootImpl.java:3687)
        at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3482)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2819)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1782)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7785)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1031)
        at android.view.Choreographer.doCallbacks(Choreographer.java:854)
        at android.view.Choreographer.doFrame(Choreographer.java:789)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1016)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:221)
        at android.app.ActivityThread.main(ActivityThread.java:7520)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
Bug TextInput Author Feedback Repro Verify on Latest Version Android

Most helpful comment

I had to set caretHidden to true and it's not crashing the app now (tested on Redmi K30).
The downside is you can't use the context menu actions. I am okay though with the trade off (for now) considering I'll be using it only on email address inputs.

<TextInput
    caretHidden
    autoCapitalize='none'
    autoCorrect={false}
    keyboardType='email-address'
    autoCompleteType='email'
/>

All 64 comments

This issue is related to: #20887

Basically, when the issue described in #20887 occurs and you can't open the context menu, if you digit an email address in a Xiaomi devices running the latest OS version it results in an app crash.

The workaround described in #20887 fixes this issue too

having the same issue =(

Same problem here. It only happens on Xiaomi devices running Android 10.

We also fixed this hacky with a hooks version of https://github.com/facebook/react-native/issues/20887#issuecomment-416775306

It works but it breaks the autoFocus prop.

const [editable, setEditable] = React.useState(false)

React.useEffect(() => {
  React.useEffect(() => {
  setTimeout(() => {
    setEditable(true)
  }, 100)
}, [])

return (
  <TextInput
    editable={editable}
    ...
  />
)

I have a user that is experiencing this same issue. Is it related to the autofocus when going to a screen? It's quite hard to replicate but had a couple of crashes when trying it myself.

It happened to me on screens without any autofocus, but sadly I had not been able to understand what the underlying cause is.

Same issue here. User gave one star review for this. 😢

I have same problem with RN 0.61.4

having same issue with RN 0.59.0

We also fixed this hacky with a hooks version of #20887 (comment)

It works but it breaks the autoFocus prop.

const [editable, setEditable] = React.useState(false)

React.useEffect(() => {
  React.useEffect(() => {
  setTimeout(() => {
    setEditable(true)
  }, 100)
}, [])

return (
  <TextInput
    editable={editable}
    ...
  />
)

I try, but cannot fix this crash.

I think found a workaround. Basically we need to set "secureTextEntry" in true and on "onLayout" method, changed the value into false.

I think @ musemind's solution fixed my problem.

Removing <item name="android:windowTranslucentStatus">true</item> from styles.xml on Android side fixes the issue.

Same problem here, none of the proposed solutions worked in my production app. I have several thousands of users so far (released a few days ago). The models MI 9 (cepheus), Redmi K20 Pro (raphael), and Mi 9T (davinci), are experiencing this issue, they can't even create an account on my app. And the 1 star review keep coming...

I had to set caretHidden to true and it's not crashing the app now (tested on Redmi K30).
The downside is you can't use the context menu actions. I am okay though with the trade off (for now) considering I'll be using it only on email address inputs.

<TextInput
    caretHidden
    autoCapitalize='none'
    autoCorrect={false}
    keyboardType='email-address'
    autoCompleteType='email'
/>

It would be great if this issue could get some attention from the React Native team. I initially fixed it using the timeout/editable workaround described here and in #20887, but since we migrated from Expo 34 to Expo 36 (React Native 0.61.5), it has returned and the workaround doesn't seem to work anymore.

This is exactly the type of bug that causes terrible user experiences, even if it's limited to a set of devices. The users impacted are quick to churn and write 1-star reviews.

Same issue here on a MI 9.
Setting caretHidden={true} is the only solution that works but really destroys the user experience.

Same here.. +1

Same issue here on a MI 9.
Setting caretHidden={true} is the only solution that works but really destroys the user experience.

Same issue here on a MI Mix 2s.

Yes even if caretHidden is a workaround, it cannot be an acceptable solution as you won't be able to see the cursor in the EditText anymore 😞

i have plan for now just use caretHidden property untill this is going to be fixed. It is not acceptable from UX point of view, but its better than crashing app and in our example, user cant even log into app.

btw. anyone has maybe function which check if device is Xiaomi ?

What about using react-native-device-info to check if it Xiaomi to enable caretHidden ?

thats my plan for now, but i don’t have Xiaomi device to check what exactly device model returns, thats why i am asking for it 🤔

I am experiencing this issue, any update for this? this happen only when the TextInput is prefilled with email value.

For temperary fix you can use like this as mentioned at https://github.com/facebook/react-native/issues/20887#issuecomment-416775306 :

..
..
constructor(props) {
    super(props)

     this.state = { editable: false }
  }

  componentDidMount() {
      setTimeout(() => { this.setState({ editable: true }) }, 100);
  }

  render() {
    return (
         ...
         ... 
            <TextInput editable={this.state.editable} />
        ...
        ...
      )
   }
}

It's temperary but it works for now. But of course it should be fixed soon as possible.

+1

I found the problem. This is a bug in the mi system on android 10, because it will have a prompt view when you type an email in the input box.When we put the input box in the scroll view, the system failed to locate the prompt view properly, causing the application to crash.If the input field is outside of the scroll view you can avoid this problem and hopefully this will help you out, this is not the optimal solution.Because it might require us to change our layout.That's what I'm going to do.

Oh, I'm sorry that the previous answer was problematic. Please remove the removeClippedSubviews property from your ScrollView component. This is the cause of the problem

Oh, I'm sorry that the previous answer was problematic. Please remove the removeClippedSubviews property from your ScrollView component. This is the cause of the problem

+1
in my case I have to set removeClippedSubviews={false}, I use FlatList, and the input is outside the screen view

setting removeClippedSubviews={false} on the container view fixed the problem for me

+1

Using Expo, the difference is that this error don't appear on logs.

just happened to experience it as well in my Redmi Note 8 Pro

The only solution that works is applying caretHidden. Try to set timeout in editable doesn't work

Hope this can get fixed asap

Someone knows if there is a workaround for the user?

We have a similar issue with users who have Xiaomi. Does anyone know how to test this without actual device? Any emulator which acting like this?

I think I found a workaround, just disable the scrolling of ScrollView when user starts editing, then everything will turn back to normal:

const [isScrollEnable, setIsScrollEnable] = useState(true)

return (
    <ScrollView scrollEnabled={isScrollEnable}>
        <TextInput
            onTouchStart={() => setIsScrollEnable(false)}
            onEndEditing={() => setIsScrollEnable(true)}
        />
    </ScrollView>
)

Keep in mind that this crash happens because of how MIUI is rendering email suggestion box - so it's not limited strictly to Xiaomi branded phones, it can happen also on phones that are made by "sister" brands like Redmi and Poco so when you are creating check if caret needs to be turned off (or any other workaround that works for you) do not include only xiaomi in check but also the rest of miui family.
I don't have any pocophone or know someone with one so I can't tell for sure what BUILD.Brand is reported there so my check looks like this

const brandsNeedingWorkaround = ['redmi', 'xiaomi', 'poco', 'pocophone']
const needsXiaomiWorkaround =
    type === 'email' &&
    brandsNeedingWorkaround.includes(getDeviceBrand().toLowerCase()) &&
    getDeviceOSVersion() > 28

I had the same problem. I had to remove
keyboardType='email-address'
to get rid of the crashing

react-native: "0.61.5", device:xiaomi MI 8 SE,android10,MIUI 11.0.3.
In my project , it worked.Just add View in outside.Maybe you can try try.

<View>
        <TextInput
               keyboardType={'email-address'}
               multiline={false}
               placeholder={'请输入邮箱'}
               onChangeText={(inputData) => {
               }}/>
</View>

    autoCompleteType='email'

Thank you @ahdzlee ! Saved me a lot of time searching for workarounds! Whew 👍

Should be fixed in next version of react native.

Based on commit 07a597a

My hack:

import { Platform, TextInput } from 'react-native';

const { Version } = Platform;

const brandsNeedingWorkaround = ['redmi', 'xiaomi', 'poco', 'pocophone'];
const needsXiaomiWorkaround = brandsNeedingWorkaround.includes(RNInfo.getBrand().toLowerCase())
    && Version > 28;

// https://github.com/facebook/react-native/issues/27204
const InputTextWrapper = forwardRef(({ onFocus, caretHidden, ...others }, ref) => {
    const [hackCaretHidden, setHackCaretHidden] = useState(needsXiaomiWorkaround ? true : caretHidden);

    const handleFocus = useCallback(() => {
        if (needsXiaomiWorkaround) {
            setHackCaretHidden(caretHidden);
        }
        if (onFocus) onFocus();
    }, [onFocus, caretHidden]);

    return (
        <TextInput
            ref={ref}
            {...others}
            onFocus={handleFocus}
            caretHidden={hackCaretHidden}
        />
    );
});

Should be fixed in next version of react native.

Based on commit 07a597a

I don't think that this is a good solution.

I think its possible to handle this problem without hide the cursor forever, like in @claudiaps hack.

Hey all! Could someone provide a repro for this with the latest template? Would be nice to have a bunch of working/crashing emails to debug

:warning: Missing Reproducible Example
:information_source: It looks like your issue is missing a reproducible example. Please provide a Snack or a repository that demonstrates the issue you are reporting in a minimal, complete, and reproducible manner.
:warning: Using Old Version
:information_source: It looks like you are using an older version of React Native. Please upgrade to the latest version, and verify if the issue persists. If it does not, 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 current release.

Hi,
I have tried with latest version of react native 0.63.1 and mobile os version MIUI Global 11.0.2

There is a solution, using caretHidden prop and pass it as true value, more explanation on StackOverflow post.

Until this issue is fixed & released, here is a simple solution for it:

const deviceName = Device.manufacturer;

    if (deviceName === "Xiaomi") {
      this.setState({
        caretHidden: true,
      });

    } else {

      this.setState({
        caretHidden: false,
      });

    }

<TextInput
        caretHidden = {this.state.caretHidden}
/>

Finally 😄 https://github.com/facebook/react-native/commit/07a597ad185c8c31ac38bdd4d022b0b880d02859

Its not only on Xiaomi (as mentioned in https://github.com/facebook/react-native/issues/27204#issuecomment-666344168), and the problem happens even when is not type email-address, because the MIUI detects email automatically.

Finally 😄 07a597a

Its not only on Xiaomi, and the problem happens even when is not type email-address but the MI UI detects an email.

Hmmm, I didn't encounter with this error on any other device. But actually when I review the commit. It's just a workaround. Not a full covered solution I think. (Because, it's a Mi UI side problem I suppose.)

But still many many thanks to who commit that solution.

Actually it's still crashing on some Mi devices because the fix https://github.com/facebook/react-native/commit/07a597ad185c8c31ac38bdd4d022b0b880d02859 only works for phones who have exactly "Xiaomi" as Manufacturer, but there are more Xiaomi brands affected (Poco, Redmi etc. and some weird unique names for some new models).

I made a pull request that should cover more devices: https://github.com/facebook/react-native/pull/30109

Feel free to add more if you see that some are missing, I just took the ones I found in my app's error reports.

Just wondering but does that fix actually work because on one of my team's test devices the issue also happens if keyboardType is set to default as soon as the user enters something that resembles an email address and from what I can see that is basically what this fix does?

Just wondering but does that fix actually work because on one of my team's test devices the issue also happens if keyboardType is set to default as soon as the user enters something that resembles an email address and from what I can see that is basically what this fix does?

Same here, the only way we can avoid crash is with caretHidden: true, just like @claudiaps suggested.

Yeah, after looking this error still exists.

import { getBrand } from "react-native-device-info"

I solved this problem with brand check and caretHidden.
If phone brand is one of these brands. I'm giving true value for the caretHidden.

const brandsNeedingWorkaround = ["redmi", "xiaomi", "poco", "pocophone"]
const needsXiaomiWorkaround = Platform.OS !== "ios" ? brandsNeedingWorkaround.includes(getBrand().toLowerCase()) : false

😞

Something really weird happened today: On the device where it crashed yesterday, it seems to work fine today without any changes regarding the input fields. We tried the caret workaround but after reverting it still worked fine (and the caret is visible and the correct keyboard input type is used).

The only thing that might remain is that I installed the dependency expo-device for the workaround but that really should not fix it. And we added alwaysBounceVertical to the ScrollView bur that is an iOS only property.

And as far as we know we did not install any updates on the device.

I tried to determine what might have fixed this but I really don't know it should now work.

Something really weird happened today: On the device where it crashed yesterday, it seems to work fine today without any changes regarding the input fields. We tried the caret workaround but after reverting it still worked fine (and the caret is visible and the correct keyboard input type is used).

The only thing that might remain is that I installed the dependency expo-device for the workaround but that really should not fix it. And we added alwaysBounceVertical to the ScrollView bur that is an iOS only property.

And as far as we know we did not install any updates on the device.

I tried to determine what might have fixed this but I really don't know it should now work.

We have noticed this behavior as well. Especially after giving the focus without crashing once, the next times it won't crash either. (Perhaps due to livereload refresh)

But when you close the app and open it again, the problem appears again.

As far as I could tell it was still working after a complete restart. However, as it is his own device and he is not working as a developer I can't do any in-depth analysis.

As it is an app we have just started developing, we will keep it in mind and hope it won't re-occur, if not we will need to re-activate the caret workaround.

Could be that some weird combination of changes fixed it which also means that it could easily re-occur.

In any case, all of the current "fixes" are workarounds and no real fixes.

However, as I doubt nearly all apps with form inputs are crashing on Xiaomi devices that do not use one of those workarounds,
I really think it has to be also react native related and not just an issue with the OS.

See comments above.

That fix does not necessarily fix it as using default keyboard type does not ensure the issue does not happen. It occurs as soon as an email address like string is inputed.
Also it does not take into account all Xiaomi UI devices.
And it is actually more of a workaround than a solution.

It took me some time to find out that if I type a . (dot) in the email input field and then clear it, after that typing the email does not crash the app anymore. So here is yet another workaround that does not include caret hiding and still working for me on Redmi 9.

const [email, setEmail] = useState(".");

useEffect(() => {
  setTimeout(() => {
    setEmail("");
  }, 1);
}, []);

It seems this issue was fixed on React native 0.63.3, could someone double-check?

https://github.com/react-native-community/releases/blob/master/CHANGELOG.md#fixed
07a597a

This issue was fixed on React native 0.63.3 go npx react-native upgrade

This issue was fixed on React native 0.63.3 go npx react-native upgrade

As mentioned by me and @AndreasA, its NOT fixed on 0.63.3, for many reasons.

Sharing our solution for this problem that uses the editable solution listed before but also works (in our tests) for autoFocus. Setting caretHidden to true does work but it makes for a jarring UI. Using the editable workaround the user experience is not changed.

This is our text field component that we use instead of calling TextInput directly:

import { Platform, TextInput } from 'react-native'
import { getBrand } from 'react-native-device-info'

const EMAIL_CRASH_WORKAROUND_BRANDS = ['m2002j9g', 'm2002j9g', 'm2004j19c', 'm2007j20cg', 'mi', 'mi', 'mix', 'poco', 'pocophone', 'redmi', 'xiaomi']
const EMAIL_CRASH_WORKAROUND_NEEDED = EMAIL_CRASH_WORKAROUND_BRANDS.includes(getBrand().toLowerCase()) && Platform.Version > 28

I stripped down our code to show the relevant methods of the component:

// allows parent to set focus to the input
focus(){
    // console.log('set focus to text field')
    // set flag so we know if focus() was called
    this.wasFocusHandlerCalled = true 
    this.input.focus()
},

// capture reference to the input
setRef(ref){
    this.input = ref
},

render(){
    let attribs = {}
    // email crash workaround
    if(EMAIL_CRASH_WORKAROUND_NEEDED){
        if(this.state && this.state.emailCrashEditable)
            attribs.editable = 'editable' in this.props ? this.props.editable : true
        else
            attribs.editable = 'editable' in this.props ? !this.props.editable : false

        // console.log(`editable for ${this.props.fieldName}:`, attribs.editable)
    }

    return (
        <TextInput              
            ref={this.setRef}
            {... this.props}
            { ... attribs} 
        />
    )
}

Finally the most important part. We toggle the editable prop of the input once the component is loaded.
We check for the autoFocus prop here and set focus if necessary.
A parent component using a ref to this component could call focus() on it so we also check if that was called and set focus if necessary:

componentDidMount(){
    // 'editable' email crash workaround 
    if(EMAIL_CRASH_WORKAROUND_NEEDED){
        setTimeout(() => {
            if(this.hasUnmounted) return // set this in componentWillUnmount

            // console.log('set emailCrashEditable')
            this.setState({ emailCrashEditable: true }, () => {
                if(this.props.autoFocus || this.wasFocusHandlerCalled){
                    // when focus was set by calling the focus handler then we need to blur it first
                    if(this.wasFocusHandlerCalled)
                        this.input.blur()

                    this.input.focus()
                }
            })
        }, 100)
    }
}

Looking forward to an actual fix for this in the core code so that we can get rid of this kludgy workaround.

Was this page helpful?
0 / 5 - 0 ratings