react-native: TextInput hangs with Samsung keyboard's Grammarly integration

UPDATE: we have now released patch releases containing fixes for:


Description

There is an issue with React Native’s TextInput component when using Samsung’s keyboard with the Grammarly integration enabled. When typing a text that is longer than a few lines, the app often hangs and becomes unresponsive, leading to a high rate of ANRs and crashes.

This issue has already been documented here and here but I’m worried that the lack of a reliable reproducer as well as the noise due to other random freeze and crashes in Android (esp. with the new Android 13 update) is preventing those issues from gaining traction and becoming a high priority.

I am therefore creating this new issue focused solely on the impact of the Samsung keyboard’s Grammarly integration on the TextInput component, with a reliable reproducer, as I believe that something can be done at the TextInput level to address it.

Here is everything we know so far:

  • Any React Native app that involves writing long text in a TextInput seem to be impacted.
  • The issue happens with the default Samsung keyboard with the Grammarly integration (aka “Suggest text corrections” aka “Writing assistant”) enabled. This integration is now enabled by default with Samsung’s new Android 13 and One UI 5.0 update, currently on a progressive rollout until the end of the year, increasing the impact of the issue. This issue can be reproduced on earlier versions of Android as well.
  • The issue does not happen when the Grammarly integration is disabled or when using a different keyboard like Gboard.
  • The issue happens sporadically when writing a paragraph of text, but it can be reliably reproduced by pasting a big wall of text (~1000 words), waiting for Grammarly to analyse it and display its green underlines, then typing.
  • The issue is a lot less impactful when using uncontrolled inputs rather than controlled inputs with a value prop. Switching to uncontrolled inputs decreased the amount of occurences of the issue in the field for us, but it’s still happening a lot. More info here.
  • The issue can still be reproduced with uncontrolled inputs, but a lot more text need to be pasted and parsed by Grammarly before the issue happens reliably.
  • The vast majority of related ANRs recorded in the field are hanging on android.text.SpannableStringBuilder
  • Profiling and analysing the callstack shows a disproportionately long android.text.DynamicLayout.reflow when reproducing the issue, as well as a higher number of calls to android.text.SpannableStringBuilder.countSpans.

You’ll find an example ANR stacktrace, recorded callstacks on different keyboards, and details of the callstacks in the toggles below.

ANR stacktrace

io.sentry.android.core.ApplicationNotResponding: Application Not Responding for at least 5000 ms.
    at java.lang.Class.isAssignableFrom(Class.java:590)
    at java.lang.Class.isInstance(Class.java:542)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:985)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:1005)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:1005)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:1005)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:1005)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968)
    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968)
    at android.text.SpannableStringBuilder.getSpans(SpannableStringBuilder.java:894)
    at android.text.SpannableStringBuilder.getSpans(SpannableStringBuilder.java:863)
    at android.text.MeasuredParagraph.buildForStaticLayout(MeasuredParagraph.java:445)
    at android.text.PrecomputedText.createMeasuredParagraphs(PrecomputedText.java:505)
    at android.text.StaticLayout.generate(StaticLayout.java:717)
    at android.text.DynamicLayout.reflow(DynamicLayout.java:612)
    at android.text.DynamicLayout$ChangeWatcher.reflow(DynamicLayout.java:1091)
    at android.text.DynamicLayout$ChangeWatcher.onSpanChanged(DynamicLayout.java:1127)
    at android.text.SpannableStringBuilder.sendSpanChanged(SpannableStringBuilder.java:1321)
    at android.text.SpannableStringBuilder.sendToSpanWatchers(SpannableStringBuilder.java:665)
    at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:594)
    at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:232)
    at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:39)
    at android.text.method.BaseKeyListener.backspaceOrForwardDelete(BaseKeyListener.java:376)
    at android.text.method.BaseKeyListener.backspace(BaseKeyListener.java:71)
    at android.text.method.BaseKeyListener.onKeyDown(BaseKeyListener.java:485)
    at android.text.method.QwertyKeyListener.onKeyDown(QwertyKeyListener.java:362)
    at com.facebook.react.views.textinput.c$b.onKeyDown(ReactEditText.java:1)
    at android.widget.TextView.doKeyDown(TextView.java:9503)
    at android.widget.TextView.onKeyDown(TextView.java:9275)
    at android.view.KeyEvent.dispatch(KeyEvent.java:3501)
    at android.view.View.dispatchKeyEvent(View.java:15384)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.widget.ScrollView.dispatchKeyEvent(ScrollView.java:738)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at g.d.o.y.dispatchKeyEvent(ReactRootView.java:4)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1978)
    at com.android.internal.policy.DecorView.superDispatchKeyEvent(DecorView.java:1091)
    at com.android.internal.policy.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1958)
    at android.app.Activity.dispatchKeyEvent(Activity.java:4324)
    at androidx.core.app.f.superDispatchKeyEvent(ComponentActivity.java:1)
    at androidx.core.view.j.e(KeyEventDispatcher.java:2)
    at androidx.core.app.f.dispatchKeyEvent(ComponentActivity.java:3)
    at androidx.appcompat.app.d.dispatchKeyEvent(AppCompatActivity.java:4)
    at c.a.o.i.dispatchKeyEvent(WindowCallbackWrapper.java:1)
    at androidx.appcompat.app.g$o.dispatchKeyEvent(AppCompatDelegateImpl.java:2)
    at c.a.o.i.dispatchKeyEvent(WindowCallbackWrapper.java:1)
    at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:915)
    at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:7870)
    at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:7678)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7027)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7084)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7050)
    at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7248)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7058)
    at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:7305)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7031)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7084)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7050)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7058)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7031)
    at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:10602)
    at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:10490)
    at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:10446)
    at android.view.ViewRootImpl$ViewRootHandler.handleMessageImpl(ViewRootImpl.java:6642)
    at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:6517)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:226)
    at android.os.Looper.loop(Looper.java:313)
    at android.app.ActivityThread.main(ActivityThread.java:8772)
    at java.lang.reflect.Method.invoke(Method.java)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

Profiling with callstacks

Samsung keyboard with controlled input

https://drive.google.com/file/d/1vecylVsRx5Hh-XRI4LfACXwO9DtOcO_h/view?usp=share_link

Untitled Untitled (1) Untitled (2)

Gboard with controlled input

https://drive.google.com/file/d/1avWBkD_NgDodccr3CW2flFX-AWpcfN3z/view?usp=share_link

Untitled (3) Untitled (4) Untitled (5)

Version

Latest version (0.70.6) but earlier ones as well

Output of npx react-native info

System:
    OS: macOS 13.0.1
    CPU: (8) arm64 Apple M1 Pro
    Memory: 91.58 MB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 18.10.0 - ~/.nvm/versions/node/v18.10.0/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v18.10.0/bin/yarn
    npm: 8.19.2 - ~/.nvm/versions/node/v18.10.0/bin/npm
    Watchman: 2022.11.28.00 - /opt/homebrew/bin/watchman
  Managers:
    CocoaPods: Not Found
  SDKs:
    iOS SDK:
      Platforms: DriverKit 22.1, iOS 16.1, macOS 13.0, tvOS 16.1, watchOS 9.1
    Android SDK: Not Found
  IDEs:
    Android Studio: Dolphin 2021.3.1 Patch 1 Dolphin 2021.3.1 Patch 1
    Xcode: 14.1/14B47b - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.17 - /usr/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.1.0 => 18.1.0
    react-native: 0.70.6 => 0.70.6
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Steps to reproduce

  • Use the linked Expo snack or theminimal-text-input-repro repo to get the app running on a Samsung device
  • Make sure that you are using the Samsung keyboard and that the Grammarly integration is enabled (it should underline in green mistakes when you write)
  • Paste a big wall of text (at least 1000 words) in the input in the app
  • Wait for Grammarly to process it and underline mistakes
  • Start typing, and pretty quickly the keyboard should slow down significantly, to the point of freezing
  • You can use the toggle at the top-right corner of the app to switch to an uncontrolled input to observe the difference it makes

For convenience, here is a wall of text you can copy to reproduce the issue. Try pasting it multiple times if pasting it once doesn’t trigger the issue straight away.

Wall of text

Remain lively hardly needed at do by. Two you fat downs fanny three. True mr gone most at. Dare as name just when with it body. Travelling inquietude she increasing off impossible the. Cottage be noisier looking to we promise on. Disposal to kindness appetite diverted learning of on raptures. Betrayed any may returned now dashwood formerly. Balls way delay shy boy man views. No so instrument discretion unsatiable to in.
Unpleasant astonished an diminution up partiality. Noisy an their of meant. Death means up civil do an offer wound of. Called square an in afraid direct. Resolution diminution conviction so mr at unpleasing simplicity no. No it as breakfast up conveying earnestly immediate principle. Him son disposed produced humoured overcame she bachelor improved. Studied however out wishing but inhabit fortune windows.
Chapter too parties its letters nor. Cheerful but whatever ladyship disposed yet judgment. Lasted answer oppose to ye months no esteem. Branched is on an ecstatic directly it. Put off continue you denoting returned juvenile. Looked person sister result mr to. Replied demands charmed do viewing ye colonel to so. Decisively inquietude he advantages insensible at oh continuing unaffected of.
Inhabit hearing perhaps on ye do no. It maids decay as there he. Smallest on suitable disposed do although blessing he juvenile in. Society or if excited forbade. Here name off yet she long sold easy whom. Differed oh cheerful procured pleasure securing suitable in. Hold rich on an he oh fine. Chapter ability shyness article welcome be do on service.
Arrived totally in as between private. Favour of so as on pretty though elinor direct. Reasonable estimating be alteration we themselves entreaties me of reasonably. Direct wished so be expect polite valley. Whose asked stand it sense no spoil to. Prudent you too his conduct feeling limited and. Side he lose paid as hope so face upon be. Goodness did suitable learning put.
Talking chamber as shewing an it minutes. Trees fully of blind do. Exquisite favourite at do extensive listening. Improve up musical welcome he. Gay attended vicinity prepared now diverted. Esteems it ye sending reached as. Longer lively her design settle tastes advice mrs off who.
In post mean shot ye. There out her child sir his lived. Design at uneasy me season of branch on praise esteem. Abilities discourse believing consisted remaining to no. Mistaken no me denoting dashwood as screened. Whence or esteem easily he on. Dissuade husbands at of no if disposal.
Bed sincerity yet therefore forfeited his certainty neglected questions. Pursuit chamber as elderly amongst on. Distant however warrant farther to of. My justice wishing prudent waiting in be. Comparison age not pianoforte increasing delightful now. Insipidity sufficient dispatched any reasonably led ask. Announcing if attachment resolution sentiments admiration me on diminution.
Imagine was you removal raising gravity. Unsatiable understood or expression dissimilar so sufficient. Its party every heard and event gay. Advice he indeed things adieus in number so uneasy. To many four fact in he fail. My hung it quit next do of. It fifteen charmed by private savings it mr. Favourable cultivated alteration entreaties yet met sympathize. Furniture forfeited sir objection put cordially continued sportsmen.
Offices parties lasting outward nothing age few resolve. Impression to discretion understood to we interested he excellence. Him remarkably use projection collecting. Going about eat forty world has round miles. Attention affection at my preferred offending shameless me if agreeable. Life lain held calm and true neat she. Much feet each so went no from. Truth began maids linen an mr to after.

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

Snack: https://snack.expo.dev/@marin-birdie/minimal-text-input-repro

Repo: https://github.com/Houguiram/minimal-text-input-repro

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 102
  • Comments: 107 (19 by maintainers)

Commits related to this issue

Most upvoted comments

We’ve escalated this in our team and someone is actively investigating root cause for a clear next step. Thanks everyone for bringing this to our attention and for all the debugging and context. We will keep you updated here.

Hey folks, we’ve shared some notes investigating this issue with #35936.

We are going to lock this thread in order to link to the new one with any future guidance and updates. edit: it looks like this disables voting so I have unlocked the issue again.

Please continue to thumbs-up this issue if you are impacted, or to share information in the new issue pertaining to root-causing or mitigating the issue.

@lunaleaps thank you. If there’s a solution found, please patch older rn versions too for those who can’t update yet to 0.69-0.70+.

We’ve must have had at least 100 one star review due to this issue. 🫤

The fuller fix for this is being validated at the moment. There were some other crashes I had to prioritize first as more generally impacting.

We received contact back from Samsung but I am not confident they will be making a comprehensive fix on their end. So, our next step is to apply the fuller change so that RN does not use formatting spans whenever it is possible not to, which puts us at parity with using the stock platform. At that point, the issue will be closed as best we are able to.

This is an area where we do really want to take the time, and validate what we are doing. The last fix regressed some basic functionality in Fabric, and during validation I have discovered issues like earlier versions of Android behaving differently than the ones we normally test on.

@lunaleaps not trying to be an ass, are there any updates? Our play store reviews are plummeting 🥺

it’s been raised already a few times and a couple of Meta engineers are trying to look more into it AFAIK. But can’t make any promises or official statements, sorry; I’m frustrated as you are that this has not been fully addressed yet 😰

I am pretty confident the crash and the ANR have the same root cause of infinitely multiplying spans. In the hang case the spans are layout effecting and lead to expensive computation. Take that away, but leave other spans, and we no longer hang but eventually crash if we have too many spans still.

I.e. by removing the hang, we don’t immediately freeze, but the same issue may cause a crash eventually (that we didn’t see before before we hung first).

I am working on a more comprehensive change here which should effect more cases (as many as possible) of multiplying spans.

Hey everyone - a quick update on this: we’ve just released:

Both contain @NickGerleman’s multi-commit fix to address this problem - early signs in 0.72 RC1 suggested that they indeed address the problem, so we hope that by upgrading to these new versions you’ll see improvements! Upgrade to those and let us know!

We are planning to also backport these fixes back to 68 and 69 - but the harsh reality is that the further back we go in the codebase, the more complicated this process of backporting gets (I literally attempted to bring back these fixes to 69 three separate times without much success 😭). We’ll keep working on this and make it land in those older versions, but it will probably take a bit longer.

@lunaleaps not trying to be an ass, are there any updates? Our play store reviews are plummeting 🥺

No actionable updates for folks yet. We are still trying to figure out what part of this issue uniquely affects React Native and where exactly we should seek support from as it intersects Samsung’s proprietary Android, Android, Grammarly, etc.

If there are actionable items for you guys and/or clear progress, we will update you.

We pushed a fix to our users that dropped our ARN from 0,81% to 0,02% and stopped complaints for now. We changed out TextInput to email-address but that broke multiline support. IMO is better than having it breaking

<TextInput
  keyboardType={isSamsungWithAndroid13 ? 'email-address' : 'default'}
/>

another possible solution we’re aiming to release this week is changing the type to visible-password, which enables “some” multiline support. I’ll post once we get it out there.

<TextInput
   keyboardType={isSamsungWithAndroid13 ? 'visible-password' : 'default'}
   {...(isSamsungWithAndroid13 && { returnKeyType: 'none' })}
 />

Hey folks. I had similar issue in Aztec text editor, and spend too much time investigating it. When Grammarly integration offers a suggestion, they replace a part of the text, with one that contains their suggestion span. Problem is - it’s pretty difficult to replace part of a heavily-styled spanned text, while maintaining original spans. Also if your EditText implementation is watching spans and reacting to them things can go crazy.

My solution was to make a wrapper around proprietary Samsung InputConnection, and manually move spans around instead of replacing text. Not sure is this will help RN implementation, but I think it was worth mentioning 😃

Any update ?

Hey everyone - a quick update on this: the 0.69.10 release is out, bringing the fixes back to the last minor in the release window: https://github.com/facebook/react-native/releases/tag/v0.69.10

We’re also backporting it to 68, will start working on that asap.

In our cases, it hangs up event if there is no flatlist on the screen at all. So this is not flatlist issue

Yes please, let’s not assume this is only FlatList related; the whole “inverted” flatlist issue was mentioned many times in other threads all with fixes and solution as mentioned above.

The issue here is MUCH bigger and related to ANY input with Samsung default keyboard.

Yes maybe uncontrolled inputs could “improve”, but it is not always possible to use uncontrolled inputs and we cannot ask our users to “turn off autocorrect”, even more so since most won’t care emailing us or asking questions, they will simply uninstall, rate poorly, and leave a ton of ANRs behind.

We are now all cheering and rooting for @lunaleaps , hoping Meta itself finds a solution to this 😂

@ruairioliverwv Same here, we’re using mentions and it ANR after a few sentences. This has been disastrous for us, we got tons of support emails from complaining users.

For those of us that use mentions, this has been a disaster. If you use children on the TextInput that updates based on state, it will crash after 4 lines of typing: “Hey who you”. Doesn’t even need to be getting up to a lot of content, crashes very quickly.

Tested on Galaxy S20, Android 13. Happening for all my android 13 users

Thanks @NickGerleman for looking into it.

Although the last part scared me a bit and I’m not sure to understand it: “[…] and during validation I have discovered issues like earlier versions of Android behaving differently than the ones we normally test on.”

=> does that mean the latest patch is actually breaking stuff on some older devices? Anything we should be aware of before releasing this temporary patch (0.69.8 in my case)?

In our cases, it hangs up event if there is no flatlist on the screen at all. So this is not flatlist issue

Hey everyone - as promised, we’ve backported the fixes to 0.68 too: https://github.com/facebook/react-native/releases/tag/v0.68.7

Please bear in mind that this 0.68 patch release was done as an exceptional measure to help address this very impactful problem. We do not expect to do more patches for this minor, as it is out of the release support window. Please upgrade to newer versions asap.


Given the general silence since this new round of patches, we are assuming that it means that the problem is indeed addressed - massive props to @NickGerleman for his work in working on this!

We’ll leave the issue open a few more days to see how things evolve just in case, after which we’ll close.

Hey everybody 👋

Quick update: the fixes from @NickGerleman are now in the 0.72 RC.1 release. We’d love if you tried that version and could confirm the fix works as expected. Once we are more confident with the changes, we will also include them in the patch releases of supported minor versions (0.71, 0.70, 0.69).

Please be considerate with directly or repeatedly pinging engineers. We will continue to provide updates as they happen.

I’m on vacation until April 16th and will not personally be replying to this thread in the meantime (I think Github should show my status as away if you try to ping?).

There have been changes commited to the main branch (to be backported soon), which should reduce the use of spans in all cases where there is not nested text of different styles. This should significantly alleviate the specific issue of Samsung keyboard infinitely cloning spans. We are not planning additional remediations for after.

More invasive approaches where we intercept or made counter edits to those made by Samsung code were considered, though I have concerns that approach may be brittle against future changes by Samsung, and we do already see complaints about things like visual glitches in Samsung proofing, where it seems like the specific above approach may introduce as a side effect.

Thank you for working on more comprehensive fix, @NickGerleman! 🙏 Android 13 is being rolled out to more and more users, and they all seem to be affected by this.

@lunaleaps can you please give an update on this? There’s been a fix that improves the situation, but only by so much. What used to be ANRs are now crashes that occur somewhat less often.

we ask our clients to disable grammerly for our application instead of disabling different type of stuff for our text editor

Any of the existing hotfix releases are safe to use across different versions of Android, unless you are using the New Architecture.

The issue effecting the new architecture is fixed instead as part of 0.72.

@madmeatballs the ANRs < 0.68.6 are now crashes. Meta is still looking for ways to fix it.

As far as I understand, these new versions still cannot be considered “fix” since they turn ANRs into crashes instead as reported by those who implemented it? So this is still under investigation @NickGerleman ?

We have seen the number of ANRs decrease whilst the number of crashes has increased. I can also confirm the top two crashes did not occur on versions of the app without the patch in 0.70.7.

image image image

Hey all -

We suspected this was a culprit for us, but when we delved in to it a bit more, we discovered this was actually a bug with inverted FlatLists that got a lot worse on Android 13.

Here’s the relevant issue: https://github.com/facebook/react-native/issues/30034

I’m posting in case others have the same underlying problem. A text input is often in the same view as an inverted FlatList, for say, instant messaging.

I could not reproduce this issue on Galaxy s21 (Android 13) with or without Grammarly on, but some of our users are reporting it. This is one of the screen records from our moderators.

321529296_6008028922561982_7436291000411312678_n.mp4

It’s quite easy to reproduce, just need to do a few text mistakes to make the Grammarly underline errors and suggest corrections, after that in a few next sentences the app will hang on. I have reproduced it on all our React Native apps with the Galaxy Z Fold 3 (android 13)

We also have significant issues with this. Disabling auto-complete isn’t a long-term option for app with users who might have English as a second language.

It really sounds like the problem stems from this Flatlist inverted problem - which has been reported multiple times. I’m currently trying to de-duplicate all the existing reports into one issue, so that we can use that as the main issue to discuss said problem. (I’ll update this one here once there’s some clarity)

I’m going to close this as no new reports specific about the TextInput issue combo that we wanted to address (TextInput, Grammarly, Samsung) have been produced - so we can consider it addressed. If you can create a new repro that does not involve an inverted FlatList, please open a new issue.

@pierroo I updated the comment, thanks for the feedback. Yes, we will release patch versions with the text input fixes for all supported minor versions – 0.69, 0.70, 0.71.

Thank you @fortmarek ! Can you confirm those will also be added in a new 0.69.9 patch as well for those in earlier versions? Because I am still facing this crash issue on 0.69.8 like many others

We’ve escalated this in our team and someone is actively investigating root cause for a clear next step. Thanks everyone for bringing this to our attention and for all the debugging and context. We will keep you updated here.

@lunaleaps Any updates on this? Thanks

@kelset sorry for spamming this, I’ve edited the comment above with more info, just in case you just read the original comment only.

We pushed a fix to our users that dropped our ARN from 0,81% to 0,02% and stopped complaints for now. We changed out TextInput to email-address but that broke multiline support. IMO is better than having it breaking

<TextInput
  keyboardType={isSamsungWithAndroid13 ? 'email-address' : 'default'}
/>

another possible solution we’re aiming to release this week is changing the type to visible-password, which enables “some” multiline support. I’ll post once we get it out there.

<TextInput
   keyboardType={isSamsungWithAndroid13 ? 'visible-password' : 'default'}
   {...(isSamsungWithAndroid13 && { returnKeyType: 'none' })}
 />

just an update here, changing the keyboard type to visible-password and adding returnKeyType: none kept the ARN low and enabled some multiline support. This will disable autocorrection, Swype keyboard, and suggested words. I can still see that our ARN is a bit higher on Samsung Android 13 devices compared to the other manufacturers and Android versions, so it’s likely that it is still happening but at a lower degree.

Settings up visible-password is not a good solution, because our users start to complain that they do not have autocorrections and autosuggestion which is a pretty necessary feature.

well, it’s a trade-off. IMO I rather have users complaining about not having autocorrections than having users complaining about crashes and freezes.

@cortinico @kelset How can we get the attention of some RN team members? This seems really urgent and as far as I can see no one is assigned to this issue.

This is my analysis of this issue based on the ANR log.

The bug reproduces on Samsung devices while using the Samsung Keyboard Grammarly integration.

The Grammarly keyboard uses the OnKeyboardActionListener (more info) to listen to the key pressed and display Grammarly suggestions.

The Samsung Grammarly integration is available starting from One UI 4.0. 
I tested the snack with Samsung device OS One UI 4.1, but the Grammarly integration is unavailable on Galaxy A12. I could not reproduce #35590 using the Grammarly keyboard. I did not try to reproduce #33139 and #30263. 
I include a superficial investigation.

  1. Text is deleted in the TextInput by pressing the Samsung keyboard backspace key. The backspace keyboard event is triggered from the Native Android API.

https://github.com/aosp-mirror/platform_frameworks_base/blob/e648d3cb91f9a0156c729ca18ec27e71f59f9ce2/core/java/android/text/method/BaseKeyListener.java#L39-L49

/**
 * Abstract base class for key listeners.
 *
 * Provides a basic foundation for entering and editing text.
 * Subclasses should override {@link #onKeyDown} and {@link #onKeyUp} to insert
 * characters as keys are pressed.
 * <p></p>
 * As for all implementations of {@link KeyListener}, this class is only concerned
 * with hardware keyboards.  Software input methods have no obligation to trigger
 * the methods in this class.
 */
  1. The ReactRootView intercepts a backspace key event. Dispatches the key event to the view located at those x/y coordinates (at g.d.o.y.dispatchKeyEvent(ReactRootView.java:4)).
  2. The view at x/y coordinates is a ReactEditText. The method onKeyDown triggers for the backspace key.

at com.facebook.react.views.textinput.c$b.onKeyDown(ReactEditText.java:1))

BaseKeyListener#backspace

Performs the action that happens when you press the {@link KeyEvent#KEYCODE_DEL} key in a {@link TextView}. If there is a selection, delete the selection; otherwise, delete the character before the cursor, if any, ALT+DEL deletes everything on the line the cursor is on.

at android.text.SpannableStringBuilder.sendSpanChanged(SpannableStringBuilder.java:1321)
at android.text.SpannableStringBuilder.sendToSpanWatchers(SpannableStringBuilder.java:665)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:594)
at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:232)
at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:39)
at android.text.method.BaseKeyListener.backspaceOrForwardDelete(BaseKeyListener.java:376)
at android.text.method.BaseKeyListener.backspace(BaseKeyListener.java:71)  
  1. After backspace is triggered, the TextInput value (span) is deleted and replaced (more in the Editable API).

    at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:594) at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:232) at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:39)

  2. Deleting and replacing the Span triggers DynamicLayout onSpanChange, which computes the new layout based on the size of the new Spans.

    at android.text.PrecomputedText.createMeasuredParagraphs(PrecomputedText.java:505) at android.text.StaticLayout.generate(StaticLayout.java:717) at android.text.DynamicLayout.reflow(DynamicLayout.java:612) at android.text.DynamicLayout$ChangeWatcher.reflow(DynamicLayout.java:1091) at android.text.DynamicLayout$ChangeWatcher.onSpanChanged(DynamicLayout.java:1127)

  3. Android iterates over each span to calculate the new size of the TextInput (createMeasuredParagraphs at Step 5). The getSpans method retrieve a specific type of span in the Text string (for example ClickableSpan).

    at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:1005) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:1005) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:1005) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:1005) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:968) at android.text.SpannableStringBuilder.getSpans(SpannableStringBuilder.java:894) at android.text.SpannableStringBuilder.getSpans(SpannableStringBuilder.java:863)

  4. The ANR triggers at this line of getSpanRec while running kind.isInstance.

io.sentry.android.core.ApplicationNotResponding: Application Not Responding for at least 5000 ms. at java.lang.Class.isAssignableFrom(Class.java:590) at java.lang.Class.isInstance(Class.java:542) at android.text.SpannableStringBuilder.getSpansRec(SpannableStringBuilder.java:985)

https://github.com/aosp-mirror/platform_frameworks_base/blob/e648d3cb91f9a0156c729ca18ec27e71f59f9ce2/core/java/android/text/SpannableStringBuilder.java#L917

    private <T> int getSpansRec(int queryStart, int queryEnd, Class<T> kind,
            int i, T[] ret, int[] priority, int[] insertionOrder, int count, boolean sort) {
        if ((i & 1) != 0) {
            // some logic
            if (spanEnd >= queryStart &&
                    (spanStart == spanEnd || queryStart == queryEnd ||
                        (spanStart != queryEnd && spanEnd != queryStart)) &&
                        (Object.class == kind || kind.isInstance(mSpans[i]))) {

This are reports for the same ANR triggered by countSpans which also implements similar logic (loop, run kind.isInstance, and trigger ARN):

Related Issues #33139 (#30263, #35155, #35590, #35350)

We are using RN 0.71.8 together with expo 48.0 and the issue is still happens to all of our Samsung users for some reason, any ideas?

I can confirm that app hangs/typing is slowed down even after upgrading RN 0.71. We are using Flatlist with inverted property. we solved it by removing inverted property and applying styles for inverting the list.

export const platformComponentStyle = {transform: [{rotate: ‘180deg’}]};

@kelset Lorenzo Sciandra FTE - Could you please let me know if there is a solution available for version 0.59, or if it’s also possible to backport the fixes?

sorry, we won’t be backporting to 0.59. It’s a very old version and our recommendation is to upgrade to newer ones. I am not aware of any workarounds that would work for that version.

If you upgrade to 0.68.7 version, the results will be the same. I was using the 0.68.5 version After changing from package.json to 0.68.7, I installed npm, cleared cache, and built.

Hi all,

I’ve been investigating for quite a few days a crash that many of my users seems to experience and that my play console displays as:

Exception java.lang.IndexOutOfBoundsException:
  at android.view.ViewGroup.removeViewsInternal (ViewGroup.java:5870)
  at android.view.ViewGroup.removeViewsInLayout (ViewGroup.java:5717)
at com.facebook.react.views.view.i.removeViewWithSubviewClippingEnabled
  at com.facebook.react.views.view.ReactClippingViewManager.removeViewAt
  at com.facebook.react.views.view.ReactClippingViewManager.removeViewAt
  at com.facebook.react.uimanager.ViewGroupManager.removeView
  at com.swmansion.reanimated.layoutReanimation.ReanimatedNativeHierarchyManager$1.run
  at com.swmansion.reanimated.layoutReanimation.ReanimatedNativeHierarchyManager.dropView
  at com.facebook.react.uimanager.o.dropView
  at com.swmansion.reanimated.layoutReanimation.ReanimatedNativeHierarchyManager.dropView
  at com.facebook.react.uimanager.o.dropView
  at com.swmansion.reanimated.layoutReanimation.ReanimatedNativeHierarchyManager.dropView
  at com.facebook.react.uimanager.o.dropView
  at com.swmansion.reanimated.layoutReanimation.ReanimatedNativeHierarchyManager.dropView
  at com.facebook.react.uimanager.o.dropView
  at com.swmansion.reanimated.layoutReanimation.ReanimatedNativeHierarchyManager.dropView
...

Considering it makes up for 50% of my crashes it’s worth looking into. But yet I was completely unable to pinpoint where it comes from, for I hadn’t changed anything in my code that could be related to that in my latest updates, EXCEPT I went from react-native 0.69.7 to the 0.69.8 that contained this textinput mitigation on samsung.

Those crashes above happen on all kind of devices (not samsung only)

And since users seems to be facing these crashes only in chat (where textinput is used), I started to make the connection.

I really don’t mean to add noise here, but if in anyway it may be related I thought I might as well mention it.

if that matters, this happens with react-native-reanimated v2.13.0 up to 2.14.4 (haven’t tested previous ones)

@NickGerleman as mentioned I hope it’s unrelated, but perhaps at a quick glance you may be able to know whether or not it could be connected; if not I will remove it to avoid extra noises.

This does not seem related as far as I can tell.

Here are the 10 (9+1) PRs that make up the fix (still being tested): https://github.com/reactwg/react-native-releases/discussions/63#discussioncomment-5519978

Thank you Nick for your excellent work, and apologies for the pings 🙏

Pinging @lunaleaps as well. The ☝️ PR by @khaykov could possibly be adapted to RN BaseInputConnection to fix crashes related to Samsung devices running Android 13.

Hey folks. I had similar issue in Aztec text editor,

Just pinging @NickGerleman in case the PR has something react-native could benefit from.

The fix I am working on is for a specific issue that only effects the Samsung keyboard. It will not create an improvement on non-Samsung devices.

Other hangs will have an unrelated root cause. The issue has had overlap in users with another, where inverted FlatList causes performance issues after the Android 11 update.

Sorry to ping you again @lunaleaps, but our Android crashes are up by 20,000%

It would be great to find a solution to this issue and I unfortunately it seems to be up to the react-native gurus at Meta.

@Phong-TH it’s impossible to apply this patch in such an old react native version

@efstathiosntonas @jgillick could anyone help me to apply this patch for 0.62? Thank you so much!

@wkwyatt I believe @matinzd was referring to the patch version, not a patch file: https://github.com/facebook/react-native/releases/tag/v0.71.2

This appears to be the fix commit: https://github.com/facebook/react-native/commit/4650ef36e3d63df6e6a31f00fcf323c53daff2d6

@kelset using 0.68.6, OneUI 5.0. Here’s a stacktrace if you want to look at: https://github.com/facebook/react-native/issues/35936#issuecomment-1411588171. Personally I’ve never seen this crash before.

edit: i’ve read the link about UI 5.1, if we trust the listed devices then not all devices that are crashing are covered eg. Note 20 Ultra

android vitals: Screenshot 2023-02-03 at 12 18 02

Yep, easy to recreate, I just need to write “Hey how you” 5 or 6 times and it crashes. Just wait for grammarly to underline each sentence before going to a new line and typing the same thing again

@matinzd Reproducing it for me sometimes takes a few paragraphs of text, and other times it takes a few sentences.

We are also seeing the issue and are receiving a lot of complaints from Samsung users who have upgraded to version 13. As more users adopt the latest version, this is going to become a major headache for support and our users.