react-native: TextInput becomes slow after writing and clearing

Environment

OS: Linux 4.16 Node: 8.11.1 Yarn: 1.6.0 npm: 6.0.0 Watchman: Not Found Xcode: N/A Android Studio: 3.1.2 AI-173.4720617

Packages: (wanted => installed) react: 16.3.2 => 16.3.2 react-native: 0.55.3 => 0.55.3

Steps to Reproduce

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

class App extends Component {
  render() {
         <View>
             <TextInput />
         </View>
   }
}
  1. Create a basic app with a single TextInput
  2. Run the app on Android, with or without debug active, also in production build (signed APK)
  3. Write in the TextInput anything, delete it and repeat the process until it becomes laggy.

Expected Behavior

TextInput should not start lagging after something has been written inside and deleted.

Actual Behavior

TextInput lags after 40-50 times you delete the input, programmatically or via keyboard, doesn’t really matter also the amount of text inside of it because happens also with a single character.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 28
  • Comments: 31 (9 by maintainers)

Commits related to this issue

Most upvoted comments

I bisected the changes between v0.54 and v0.55, and found the commit that causes this issue: it’s 5898817fc “Implement letterSpacing on Android >= 5.0”. And indeed reverting that change fixes the issue.

I just sent #19645 with a revert of that change.

The test app I used to repro the issue follows @simone201 's report, with @miguelespinoza 's addition. I

  1. ran react-native init
  2. converted it to use a RN version from Git, following the docs (this required rather more fiddling than it looks, but that’s for another day)
  3. edited App.js to look like this:
import React, { Component } from 'react';
import { TextInput, View } from 'react-native';

type Props = {};
export default class App extends Component<Props> {
  state = {
    value: '',
  };

  handleTextChange = (value) => {
    this.setState({ value });
  };

  render() {
    return (
      <View>
        <TextInput
           value={this.state.value}
           onChangeText={this.handleTextChange}
         />
      </View>
    );
  }
}

Pending #19645 , others who are hitting this issue may be able to fix it by applying that patch to the RN version they use. That’s what I expect to do.

One other inference from identifying the commit that caused it: that commit was intended to have an effect mainly on Android 5.0 Lollipop and up. It’s likely that this issue only happens there. (My testing was on Android 8.1 Oreo.)

Thanks to @simone201 and @miguelespinoza for their detailed descriptions of the issue! These were very helpful in pinning down in the first place the mysterious issue we were seeing in our app, and in the bisection I just did to locate the cause within RN.

@gnprice it appears there was a change request on #19645. We could be nearing the end of this nightmare. Any chance you could address it? I think any company with an app on Android that has a text input (every app ever) would be eternally grateful.

Here’s what I wrote at https://github.com/facebook/react-native/pull/19645#issuecomment-400507673 when I figured out the bug that PR fixed (which was introduced between v0.54 and v0.55 and caused the extreme version of this problem, as described in the original reports in this thread – for short, “bug #19126”):

[This buggy NaN comparison] means every text shadow node will create a CustomLetterSpacingSpan around its contents, even when the letterSpacing prop was never set. It must be that we’re somehow ending up with large numbers of these shadow nodes – that sounds like a bug in itself, but I guess it’s normally low-impact – and this condition is failing to prune them from causing a bunch of work here.

So this #19126 (i.e., the bug that’s fixed in v0.56 by #19645) was basically a multiplier on the effect of some other, underlying bug. I’ll file an issue for the underlying bug in a moment, so it’ll have a number too.

As far as I know, nobody had previously reported the underlying bug before #19126 made its effects so severe. But now with @s-nel 's detailed observations I think we’re looking at the same underlying bug.

I don’t know anything more about what causes it; even the hypothesis that we’re ending up with large numbers of text shadow nodes is just an inference from how it interacts with the line fixed in #19645. More debugging required. Which I probably won’t do because the remaining symptoms are no longer one of the top issues in our own app, but I hope somebody else will!

@gnprice can this be cherrypicked/backported to 0.55.x? 🙏

0.56 will take another month or so to be released, isn’t it?

This is a really terrible bug. As we didn’t know how to fix it (besides from downgrading RN) we switched our inputs to be uncontrolled and that stopped the slowdown. However, on iOS we needed to apply this fix to get the text input to clear in cases such as chat: https://github.com/facebook/react-native/pull/18278.

I’ve filed #20119 with @s-nel 's report and other details from this thread. @shafy @alp1396 @stueynet and anyone else, I recommend we continue the discussion of the still-open issue there. Thanks for all the detailed reports!

After some further debugging today (details on #19645), the fix is reduced to a very simple one! Just this line:

       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-        if (textShadowNode.mLetterSpacing != Float.NaN) {
+        if (!Float.isNaN(textShadowNode.mLetterSpacing)) {
           ops.add(new SetSpanOperation(

Hopefully that’ll make it into master soon and get cherry-picked into 0.56.

I tried spinning up a fresh react-native init project, attempting to isolate the issue.

This seems to only be an issue with controlled TextInput from what I can tell.

Seems like a very fundamental issue as <TextInput> is basically unusable because of this. I hope we could at least figure out what the source of the problem is.

Pending #19645 , others who are hitting this issue may be able to fix it by applying that patch to the RN version they use. That’s what I expect to do.

Oh, I should add – here’s a version of that patch on top of the latest release (v0.55.4): https://github.com/gnprice/react-native/commit/a8c65a686c12cd0b7e9a69ed7f1f21efee4cae84

(The version in my PR is rebased on top of master, which involved resolving a few merge conflicts from unrelated changes.)

Ive checked (my local file - node_modules/react-native…ReactBaseTextShadowNode.java) that changes from many PR (seen above) are already IN RN 0.56. But in fact, the problem for me hasn’t been solved. Tested on 2 different xiaomi and 1 samsung devices. All inputs are going slowdown after delete pressed many times. By the way, app running on device emulator works prefect as well!

@s-nel yeah same here

@gianpaj Looks like it’s slated for a cherry-pick into 0.56: https://github.com/react-native-community/react-native-releases/issues/14#issuecomment-400954904

For 0.55.x, there’s a discussion over here on whether it makes sense for the RN maintainers to publish further releases in that series: https://github.com/react-native-community/react-native-releases/issues/11

@gianpaj the final 0.56 release will be published Monday. You can request a 0.56 cherry pick over at https://github.com/react-native-community/react-native-releases, otherwise this is scheduled to hit the next release, 0.57, which will also be available next week.

Based on the GIF I would say it’s an issue with the TextInput JS code. Maybe some heavy calculations that’s blocking the JS thread. I don’t have a chance to dig deeper.

I currently had to downgrade to RN 54. But this is temporary. Right now this is blocking issue. Not sure how we can escalate this