TextInput hangs with Samsung keyboard's Grammarly integration
See original GitHub issueDescription
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 toandroid.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



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



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 the
minimal-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
Issue Analytics
- State:
- Created 9 months ago
- Reactions:20
- Comments:11
@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