Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
543 views
in Technique[技术] by (71.8m points)

javascript - How to use forwardRef with FunctionComponent in React Native using TypeScript

I've looked at many of the docs and examples but I still can't seem to quite understand how to use forwardRef with a functional component with TypeScript in React Native. Below is an example where I create a MyCustomComponent with a custom function that I try to call from the parent by creating a ref. However, since the ref is incorrectly defined and null, I obviously get an error message telling me that the function doesn't exist. Please help me understand how to properly use forwardRef in React Native. Thanks in advance!

interface MyCustomComponentProps {
  title: string
}

const MyCustomComponent: React.FunctionComponent<MyCustomComponentProps> = React.forwardRef((props, ref) => {
  const coolAlert = () => {
    Alert.alert('Hey!', 'This was called from MyCustomComponent')
  }
  return (
    <View>
      <Text>{props.title}</Text>
    </View>
  )
})

export default function App () {
  const MyCustomComponentRef = useRef()
  return (
    <SafeAreaView>
      <MyCustomComponent ref={MyCustomComponentRef} title='Hello World' />
      <TouchableOpacity
        onPress={() => {
          MyCustomComponentRef.coolAlert()
        }}>
        <Text>Click Me</Text>
      </TouchableOpacity>
    </SafeAreaView>
  )
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Forwarding the Ref

Refs can be really confusing because there are multiple ways to handle them and because people aren't aware of the difference between the ref object (React.MutableRefObject or React.RefObject) and the ref value, which is stored on the .current property of the ref object. You've made that mistake here, along with some missing or incorrect typescript types.

useRef<T> is a generic hook where the value T tells up what type of value will be stored. We need to tell App that we intend to store something with a coolAlert method. Actually we'll see later on that we need our ref to be immutable so we we'll use createRef<T> instead.

interface MyRef {
  coolAlert(): void;
}

const MyCustomComponentRef = createRef<MyRef>();

When we call onPress, we need to access the current value of the ref object. By adding the generic to createRef, typescript already knows that this value is either MyRef or undefined. We can call coolAlert with the optional chaining ?. operator.

onPress={() => MyCustomComponentRef.current?.coolAlert()}

Now we need to do some work on MyCustomComponent. You've erred by assigning it the type React.FunctionComponent<MyCustomComponentProps> because a function component doesn't have the knowledge about ref forwarding that we need.

function forwardRef<T, P = {}>(Component: RefForwardingComponent<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;

The type for MyCustomComponent should be that complicated return type from forwardRef. But we don't need to assign that type ourselves, we just need to pass the generics T and P to the forwardRef function call. T is the type of the ref and P is the type of the props.

const MyCustomComponent = React.forwardRef<MyRef, MyCustomComponentProps>(...

Ok so we got rid of all the typescript errors! Yay! Except...hold up. It doesn't actually do anything. All of that and it still doesn't work. I hate refs. Refs are bad.

Using the Ref

We forwarded the ref to MyCustomComponent, who now has access to the forwarded ref and can attach it to a DOM component. But we don't want it attached to the DOM element, we want it attached to MyCustomComponent. But we can't really do that.

By default, you may not use the ref attribute on function components because they don’t have instances [docs]

We have to make use of a hook called useImperativeHandle which feels like a hack solution and even the docs say "don't do this". Yup, I hate refs.

useImperativeHandle customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with forwardRef. [docs]

We have to expose our coolAlert method through useImperativeHandle.

useImperativeHandle(ref , () => ({coolAlert}));

And now it actually works, finally!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...