React-native: Invoking TextInput methods via ref produces errors.

Created on 31 Mar 2020  Β·  21Comments  Β·  Source: facebook/react-native

Description

After invoking the focus method of the TextInput component of react native this one shows.
Tried other methods based on the documentation. different error shows per method..

image

React Native version:

System:
OS: macOS Mojave 10.14.6
CPU: (4) x64 Intel(R) Core(TM) i5-4570R CPU @ 2.70GHz
Memory: 468.52 MB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 10.15.3 - ~/.nvm/versions/node/v10.15.3/bin/node
Yarn: 1.22.0 - /usr/local/bin/yarn
npm: 6.4.1 - ~/.nvm/versions/node/v10.15.3/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 13.1, DriverKit 19.0, macOS 10.15, tvOS 13.0, watchOS 6.0
Android SDK:
API Levels: 22, 23, 24, 26, 27, 28, 29
Build Tools: 23.0.1, 26.0.3, 27.0.0, 27.0.1, 27.0.3, 28.0.0, 28.0.3, 29.0.0
System Images: android-26 | Google Play Intel x86 Atom, android-27 | Google Play Intel x86 Atom, android-28 | Intel x86 Atom_64, android-28 | Google APIs Intel x86 Atom, android-28 | Google Play Intel x86 Atom, android-29 | Intel x86 Atom, android-29 | Intel x86 Atom_64, android-29 | Google APIs Intel x86 Atom, android-29 | Google APIs Intel x86 Atom_64, android-29 | Google Play Intel x86 Atom, android-29 | Google Play Intel x86 Atom_64
Android NDK: Not Found
IDEs:
Android Studio: 3.4 AI-183.5429.30.34.5452501
Xcode: 11.1/11A1027 - /usr/bin/xcodebuild
Languages:
Python: 2.7.16 - /usr/local/bin/python
npmPackages:
@react-native-community/cli: Not Found
react: 16.11.0 => 16.11.0
react-native: 0.62.0 => 0.62.0
npmGlobalPackages:
react-native: Not Found

Steps To Reproduce

Given the following code:

    inputField = React.createRef();

    componentDidMount() {
          setTimeout(this.inputField.current.focus, 200);
    }

     render() {
           <TextInput
                ref={this.inputField}
                {...otherProps}
            />
     }

Expected Results

Should focus on the input and then show the keyboard.

TextInput

Most helpful comment

This happened to me as well after updating from 0.61.5 to 0.62

All 21 comments

This happened to me as well after updating from 0.61.5 to 0.62

Same. I came from react-native v0.59.9 then upgraded to 0.62.0. Didn't encounter this one on previous version..

Same here. Because of these lines:
https://github.com/facebook/react-native/blob/3a11f0536ea65b87dc0f006665f16a87cfa14b5e/Libraries/Components/TextInput/TextInput.js#L946-L979
with the following type definition:
https://github.com/facebook/react-native/blob/3a11f0536ea65b87dc0f006665f16a87cfa14b5e/Libraries/Components/TextInput/TextInput.js#L694-L698
I think most of these changes were introduced here: https://github.com/facebook/react-native/commit/bbc5c35a61cd3af47ccb2dc62430e4b6a4d4e08f
more specifically: https://github.com/facebook/react-native/commit/bbc5c35a61cd3af47ccb2dc62430e4b6a4d4e08f#diff-b48972356bc8dca4a00747d002fc3dd5L909-L937

I suppose only clear and isFocused still work. Kinda shitty that this quite major change is not explicitly mentioned in the changelog. The entry for the commit above reads TextInput now uses forwardRef allowing it to be used directly by new APIs requiring a host component. (bbc5c35a61 by @TheSavior)

Let's maybe tag @TheSavior

Thanks for the tag. This wasn't an intentional breaking change.

The repro has the code:

setTimeout(this.inputField.current.focus, 200);

Does it work if you bind the this?

setTimeout(this.inputField.current.focus.bind(this.inputField.current), 200);

Or call the function instead of passing the function?

setTimeout(() => {
  this.inputField.current.focus();
}, 200);

Thanks for the tag. This wasn't an intentional breaking change.

The repro has the code:

setTimeout(this.inputField.current.focus, 200);

Does it work if you bind the this?

setTimeout(this.inputField.current.focus.bind(this.inputField.current), 200);

Or call the function instead of passing the function?

setTimeout(() => {
  this.inputField.current.focus();
}, 200);

Hey man!
The code you suggested actually works. I'm a little bugged of why
setTimeout(this.inputField.current.focus, 200);
isn't working while the last one

setTimeout(() => {
  this.inputField.current.focus();
}, 200);

is working?.. Am i missing some JS/ES6 basics?

Anyway thank you.

Thank you also @TuurDutoit for pointing out the change in the codebase didn't have the time to check that one out and I immediately created an issue because i thought that, if there is some changes, it is somehow be indicated in the changelog or at least at the 0.62 documentation..

Can you show me in an expo snack an example that worked on 0.61 but not 0.62? It’s possible that when this was refactored we need to bind it. In which case we need to land this fix and get it picked into a 0.62 patch release.

I’ll leave this open for now because this is potentially an issue in 0.62

v0.61: https://github.com/TuurDutoit/rn-text-input-61
v0.62: https://github.com/TuurDutoit/rn-text-input-62

It seems that it does work on 62, because the methods are on the prototype chain. However, the Flow types are failing for them; that's what set me off in the first place. If you open the v0.62 in an editor, you'll get a Flow error on this line: https://github.com/TuurDutoit/rn-text-input-62/blob/ba5a5b05e39d524bc2df8d8dca34a5e9bf951b5b/App.js#L17

Cannot call `textInputRef.current.focus` because: Either property `focus` is missing in  `AbstractComponent` [1]. Or property `focus` is missing in  object type [2].

I am also encountering errors on isFocused and clear methods not working

Got this error when try to test my component that has a TextInput TypeError: textInput.instance.isFocused is not a function

Shouldn't it be textInput.current if it's a ref?

@TuurDutoit textInput.instance.isFocused is part of my test code. Not src code

src.js

const mobileNumberRef = useRef<TextInput>(null);
<TextInput testID={"mobileNumberTextInput"} ref={mobileNumberRef} .../>

test.js

const { queryByTestId } = render(<SomeScreen />);
const textInput = queryByTestId("mobileNumberTextInput")!;
expect(textInput.instance.isFocused()).toEqual(true); // fails due to TypeError: textInput.instance.isFocused is not a function

Used to work on RN 0.61.5. Broke on 0.62.1

@hxiongg, your issue is different. It looks like this fix didn't get picked to 0.62, I've requested it get picked here: https://github.com/react-native-community/releases/issues/184#issuecomment-609903229

@tuurDutoit thanks for the demo. I've run it on snack (pre 62), master, your 62 project, and a new 62 project, and I haven't gotten it to give me that error in any example...

This is the output from Flow ran on the rn-text-input-62 project:

Error β”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆ App.js:17:28

Cannot call textInputRef.current.focus because:
 β€’ Either property focus is missing in AbstractComponent [1].
 β€’ Or property focus is missing in object type [2].

     App.js
       14β”‚   const focusTextInput = useCallback(() => {
       15β”‚     console.log(textInputRef.current);
       16β”‚     if (textInputRef.current != null) {
       17β”‚       textInputRef.current.focus();
       18β”‚     }
       19β”‚   }, [textInputRef]);
       20β”‚

     node_modules/react-native/Libraries/Components/TextInput/TextInput.js
 [1] 1162β”‚ module.exports = ((ExportedForwardRef: any): React.AbstractComponent<
     1163β”‚   React.ElementConfig<typeof InternalTextInput>,
     1164β”‚   $ReadOnly<{|
     1165β”‚     ...React.ElementRef<HostComponent<mixed>>,
     1166β”‚     ...ImperativeMethods,
     1167β”‚   |}>,
     1168β”‚ > &
 [2] 1169β”‚   TextInputComponentStatics);


Error β”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆ
node_modules/react-native/Libraries/Modal/RCTModalHostViewNativeComponent.js:95:16

Cannot resolve name BubblingEventHandler.

     92β”‚    *
     93β”‚    * See https://facebook.github.io/react-native/docs/modal.html#ondismiss
     94β”‚    */
     95β”‚   onDismiss?: ?BubblingEventHandler<null>,
     96β”‚
     97β”‚   /**
     98β”‚    * Deprecated. Use the `animationType` prop instead.


Error β”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆ
node_modules/react-native/Libraries/YellowBox/UI/YellowBoxInspectorHeader.js:45:18

Cannot create YellowBoxInspectorHeaderButton element because number [1] is
incompatible with string [2] in property image.

     node_modules/react-native/Libraries/YellowBox/UI/YellowBoxInspectorHeader.js
     40β”‚   return (
     41β”‚     <SafeAreaView style={styles.root}>
     42β”‚       <View style={styles.header}>
     43β”‚         <YellowBoxInspectorHeaderButton
     44β”‚           disabled={props.warnings[prevIndex] == null}
     45β”‚           image={require('../../LogBox/UI/LogBoxImages/chevron-left.png')}
     46β”‚           onPress={() => props.onSelectIndex(prevIndex)}
     47β”‚         />
     48β”‚         <View style={styles.headerTitle}>
     49β”‚           <Text style={styles.headerTitleText}>{titleText}</Text>
     50β”‚         </View>
       :
 [2] 64β”‚     image: string,

     node_modules/react-native/Libraries/Image/RelativeImageStub.js
 [1] 28β”‚ }): number);


Error β”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆ
node_modules/react-native/Libraries/YellowBox/UI/YellowBoxInspectorHeader.js:53:18

Cannot create YellowBoxInspectorHeaderButton element because number [1] is
incompatible with string [2] in property image.

     node_modules/react-native/Libraries/YellowBox/UI/YellowBoxInspectorHeader.js
     48β”‚         <View style={styles.headerTitle}>
     49β”‚           <Text style={styles.headerTitleText}>{titleText}</Text>
     50β”‚         </View>
     51β”‚         <YellowBoxInspectorHeaderButton
     52β”‚           disabled={props.warnings[nextIndex] == null}
     53β”‚           image={require('../../LogBox/UI/LogBoxImages/chevron-right.png')}
     54β”‚           onPress={() => props.onSelectIndex(nextIndex)}
     55β”‚         />
     56β”‚       </View>
     57β”‚     </SafeAreaView>
     58β”‚   );
       :
 [2] 64β”‚     image: string,

     node_modules/react-native/Libraries/Image/RelativeImageStub.js
 [1] 28β”‚ }): number);


Error β”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆβ”ˆ
node_modules/react-native/Libraries/YellowBox/UI/YellowBoxInspectorHeader.js:76:8

Cannot create Image element because:
 β€’ Either string [1] is incompatible with number [2] in property source.
 β€’ Or object type [3] is not a React component.

     node_modules/react-native/Libraries/YellowBox/UI/YellowBoxInspectorHeader.js
 [1]  64β”‚     image: string,
        :
      73β”‚     onPress={props.disabled ? null : props.onPress}
      74β”‚     style={styles.headerButton}>
      75β”‚     {props.disabled ? null : (
      76β”‚       <Image source={props.image} style={styles.headerButtonImage} />
      77β”‚     )}
      78β”‚   </YellowBoxPressable>
      79β”‚ );

     node_modules/react-native/Libraries/Image/Image.ios.js
 [3] 210β”‚   ImageComponentStatics);

     node_modules/react-native/Libraries/Image/ImageSource.js
 [2]  85β”‚ export type ImageSource = ImageURISource | number | Array<ImageURISource>;



Found 5 errors

Only showing the most relevant union/intersection branches.
To see all branches, re-run Flow with --show-all-branches

This is what I did:

$ git clone [email protected]:TuurDutoit/rn-text-input-62.git
$ cd rn-text-input-62
$ yarn
$ node_modules/.bin/flow

Oh, this is a flow issue? I thought it was a runtime issue you were reporting

Alright, I think there are three different problems mentioned in this thread.

  1. @trglairnarra's original issue, comment with work around here. Runtime error, calling setTimeout(this.inputField.current.focus, 200); gives error undefined is not a function (evaluating this._nativeTag)

    1. This is because TextInput changed from using NativeMethodsMixin and createReactClass which autobound every method, to using the standard HostComponent which doesn't have these methods bound. This is now consistent with the other components we have in core. I agree that this is a breaking change. I need to think about whether we should make all of these functions bound for every component.

    2. Babel transpiles class properties with arrow functions to functions defined in the constructor and thus they aren't on the prototype. As this would be terrible for memory this won't get fixed and is considered a breaking change.

  2. @TuurDutoit reports running Flow gives an error when calling focus

    1. I'm not sure why this is but I was able to repro it. I don't get this error on master.

  3. @hxiongg reports that jest tests fail to call isFocused

    1. This was fixed and requested for a pick

I'm a little bugged of why setTimeout(this.inputField.current.focus, 200); isn't working. Am i missing some JS/ES6 basics?

@trglairnarra, I wouldn't say the issue is part of the basics, but it is a common JavaScript issue. See this MDN page for more information: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#The_this_problem

@TheSavior Not sure if this is the cause of this issue, but I just observed a weird behaviour for ref prop on TextInput that happens on v0.62.2, but not on v0.61.5. If I give a function to this prop, on v0.62.2 on each re-render of the app that function is called 1 time with null and 1 time with the actual ref to the input (does that mean that TextInput is unmounted and mounted again?):
v0-62-2
This does not happen on v0.61.5, where the function is called only once:
v0-61-5

This is the App component:

const App = () => {
  const [count, setCount] = useState(0);
  return (
    <SafeAreaView style={styles.container}>
      <TextInput style={styles.textInput} ref={logRef} />
      <TouchableOpacity
        style={styles.button}
        onPress={() => setCount((x) => x + 1)}>
        <Text>Increment {count}</Text>
      </TouchableOpacity>
    </SafeAreaView>
  );
};

You can find the full project for v0.62.2 here: https://github.com/mlazari/TextInputTest/tree/v0.62.2
And for v0.61.5 here: https://github.com/mlazari/TextInputTest/tree/v0.61.5

Both are created from scratch with npx react-native init TextInputTest --version 0.62.2 / npx react-native init TextInputTest --version 0.61.5, so this is not a RN upgrade issue.

Any updates on this? I'm getting the same error while trying to call textInputRef.current.isFocused()

@nicolas-amabile it isn't clear which error you are saying you are getting.

See this comment where I broke down the three different types of errors reported in this thread: https://github.com/facebook/react-native/issues/28459#issuecomment-609957836

As the error reported in the original post is a known breaking change due to the functions not being bound anymore to be consistent with other functions in React Native, I'm going to close this post.

If you have errors unrelated to the OP please open a new issue with context.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  Β·  3Comments

oney picture oney  Β·  3Comments

phongyewtong picture phongyewtong  Β·  3Comments

anchetaWern picture anchetaWern  Β·  3Comments

axelg12 picture axelg12  Β·  3Comments