facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
118.51k stars 24.27k forks source link

🐛 TextInput:Function components cannot be given refs #44861

Closed storyicon closed 1 month ago

storyicon commented 3 months ago

Description

When I use ref on TextInput, this problem always occurs stably:

ERROR  Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Check the render method of `CssInterop.TextInput`.
    in InternalTextInput (created by CssInterop.TextInput)
    in CssInterop.TextInput (created by Component)
    in RCTView (created by CssInterop.View)
    in CssInterop.View (created by Component)
    in Component
    in RCTView (created by CssInterop.View)
    in CssInterop.View
    in RCTView (created by CssInterop.View)
    in CssInterop.View
    in RCTView (created by CssInterop.View)
    in CssInterop.View
    in RCTView (created by CssInterop.View)
    in CssInterop.View (created by AppContainer)
    in RCTView (created by CssInterop.View)
    in CssInterop.View (created by AppContainer)
    in AppContainer (created by Modal)
    in RCTView (created by CssInterop.View)
    in CssInterop.View (created by Modal)
    in VirtualizedListContextResetter (created by Modal)
    in RCTModalHostView (created by Modal)

Steps to reproduce

import { TextInput } from 'react-native'
import { useRef } from 'react'
import { View } from 'react-native'
export default function Component() {
    const textRef = useRef<TextInput>(null)
    return (
        <View>
            <TextInput ref={(ref) => (textRef.current = ref)}></TextInput>
        </View>
    )
}

React Native Version

0.74.1

Affected Platforms

Runtime - Android

Output of npx react-native info

System:
  OS: Windows 11 10.0.22631
  CPU: (12) x64 Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz
  Memory: 1.88 GB / 15.96 GB
Binaries:
  Node:
    version: 22.2.0
    path: D:\nodejs\node.EXE
  Yarn:
    version: 1.22.10
    path: F:\nodejs\node_global\yarn.CMD
  npm:
    version: 7.19.0
    path: F:\nodejs\node_global\npm.CMD
  Watchman: Not Found
SDKs:
  Android SDK: Not Found
  Windows SDK:
    AllowDevelopmentWithoutDevLicense: Enabled
    AllowAllTrustedApps: Enabled
    Versions:
      - 10.0.19041.0
IDEs:
  Android Studio: Not Found
  Visual Studio: Not Found
Languages:
  Java:
    version: 17.0.11
    path: C:\Program Files\Microsoft\jdk-17.0.11.9-hotspot\bin\javac.EXE
  Ruby: Not Found
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.74.1
    wanted: 0.74.1
  react-native-windows: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: Not found
  newArchEnabled: Not found
iOS:
  hermesEnabled: Not found
  newArchEnabled: Not found

Stacktrace or Logs

ERROR  Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Check the render method of `CssInterop.TextInput`.
    in InternalTextInput (created by CssInterop.TextInput)
    in CssInterop.TextInput (created by Component)
    in RCTView (created by CssInterop.View)
    in CssInterop.View (created by Component)
    in Component
    in RCTView (created by CssInterop.View)
    in CssInterop.View
    in RCTView (created by CssInterop.View)
    in CssInterop.View
    in RCTView (created by CssInterop.View)
    in CssInterop.View
    in RCTView (created by CssInterop.View)
    in CssInterop.View (created by AppContainer)
    in RCTView (created by CssInterop.View)
    in CssInterop.View (created by AppContainer)
    in AppContainer (created by Modal)
    in RCTView (created by CssInterop.View)
    in CssInterop.View (created by Modal)
    in VirtualizedListContextResetter (created by Modal)
    in RCTModalHostView (created by Modal)

Reproducer

https://snack.expo.dev/@storyicon/honest-yellow-pretzel

Screenshots and Videos

image

github-actions[bot] commented 3 months ago
:warning: Newer Version of React Native is Available!
:information_source: You are on a supported minor version, but it looks like there's a newer patch available - 0.74.2. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, 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 most recent releases.
cortinico commented 3 months ago

ERROR Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

The error message is pretty self-explanatory. What is not clear about it?

storyicon commented 3 months ago

I don't believe this error should exist. The TextInput component itself provides the ref property. I've encountered this warning in many mature libraries, which makes me doubt its necessity. Do you have any suggestions for eliminating this warning in the example I provided? @cortinico

gitSirzh commented 3 months ago

It should be written like this

import { TextInput } from 'react-native'
import { useRef } from 'react'
import { View } from 'react-native'
export default function Component() {
    const textRef = useRef<TextInput>(null)
    return (
        <View>
            <TextInput ref={textRef} />
        </View>
    )
}
fabOnReact commented 2 months ago

SOLUTION FOR MY ISSUE

In my case, the app was multiplatform (web, desktop, mobile) with files named:

I did not notice that there was a file at the root MyButton/index.tsx which would render MyButton/MyButton/index.tsx or MyButton/MyButton/index.native.tsx.

I did not add the ref to the component MyButton/index.tsx so for this reason the ref was not forwarded. Somehow having the same name of the file for web, I got sidetracked and thought was the same file.

PREVIOUS COMMENTS

react-native 0.73.4

I'm experiencing this issue.

I follow the react native docs on direct-manipulation. My implementation is exactly as in the example Composite components and setNativeProps, but the ref is not forwarded (or the API is set private) and I don't have access to the native view method setNativeProps.

In my use case, I need ref to a native View to call setNativeProps.

There must be some check to detect if the component is stateless, maybe this check is failing. Because I wrapped the component in a forwardRef as by the instructions.
https://github.com/facebook/react-native/issues/16247#issuecomment-341327893

for example, code similar to this will not work in my application. I did not try to reproduce with a minimum repro, but hopefully I'll get the opportunity to do that.

MyButton.js

function MyButton(props, ref) {
    return (
        <View ref={ref} />
    );
}
export default forwardRef(MyButton);

MyContainer.js

function MyContainer(props) {
   const myRef = useRef();
   const hide = () => {
       if (myRef.current) {
           myRef.current.setNativeProps({opacity: 0});
       }
   }
   return (
      <TouchableOpacity onPress={() => hide()}>
          <MyButton ref={myRef} />
       </TouchableOpacity>
    )
}

while this works:

MyButton.js

function MyButton(props, ref) {
    return (
        <View ref={props.myButtonRef} />
    );
}
export default forwardRef(MyButton);

MyContainer.js

function MyContainer(props) {
   const myRef = useRef();
   const hide = () => {
       if (myRef.current) {
           myRef.current.setNativeProps({opacity: 0});
       }
   }
   return (
      <TouchableOpacity onPress={() => hide()}>
          <MyButton myButtonRef={myRef} />
       </TouchableOpacity>
    )
}

I did not have the time to reproduce it outside of my application with a Minimum Reproducible Example. This is just and example, but I believe the ref is getting removed.

I receive the warning functional components cannot be given refs, but the react-native documentation instruct us to use forward ref with functional components, to get a hold of the native view, so I believe here react-native is removing the ref, in fact if I set the ref without using the ref keyword on this functional/forwardRef component, it works.

I believe there is a check in the code, which removes the ref, but some of the conditions for removing this ref are failing.

The conditions maybe could be, if the component is a forwardRef, even if functional component, don't remove the ref, otherwise the ref is not forwarded.

UPDATE

I used useImperativeHandle to expose each method of the ref, but still does not work using the ref keyword from the functional component, but it does work if I pass the ref as innerRef prop instead of ref prop.

storyicon commented 1 month ago

I believe I've figured out what caused the issue. It was related to NativeWind and the package https://www.npmjs.com/package/react-native-css-interop. After uninstalling it, the problem no longer occurs.