How to use the useRef hook with forwardRef and typescript!!!
Learn how to avoid the useRef "gotcha's"
Why use ref’s to begin with?
According to React’s new documentation, using refs will allow your application to avoid unnecessary new renders. The docs explain it like this…
When you want a component to “remember” some information, but you don’t want that information to trigger new renders, you can use a ref.
You may be tempted to start replacing state with refs but I encourage you not to do so. Click here to learn best practices when deciding between using refs or state. A common use case for refs inside a React Native project is when a user is filling out a form. While a user fills out a form each input should come to “focus”. Lets use our form from the last article on creating a reusable TextInput component to demonstrate this idea.
Notice how the last name field is “focused” with a blue cursor after the user (which was me) hits the next button. This isn’t the default behavior of the TextInput component in React Native apps at the time of this writing. The last name field is focused because of the code block below 👇🏾
const handleFocus = (index: number) => {
refs.current[index].focus();
};
Without this block of code, every time the user hit the “next” button or whatever value you give to the returnKeyType prop, they would be given no indicator of the next input to fill out. This type of behavior would lead to a poor user experience.
How to implement a ref for a good user experience
Lets start with the code block I mentioned earlier but in full context.
Lets break the core functionality of this code down beginning with the useRef hook.
useRef hook
React provides us with the useRef hook in order to “escape” the typical reactive nature of components. This is exactly want we need for our use case. When the user hits the next button, there’s no need for a re render. Why? Because no state has been changed. The state changes happen when the user starts typing not when the input is focused. Our goal is to keep track of each input so we can provide the proper UX experience.
Types
To add the appropriate typing to our useRef hook, we need to pass the TextInput component to our hook with the array symbol. This tells typescript that were expecting to store an array of TextInput components. We initialize our ref with an empty array to avoid the error:
Can't set property 0 of undefined
storing refs
In order to “store” our TextInputs for future use, we need to use the ref property on each input. This property takes a function whose parameter “element” represents the current component with the ref attached. After attaching a ref prop to each component you can imagine our refs object to look something like this:
{ current: [<TextInput1/>, <TextInput2/>,<TextInput3/>,…] }
onSubmitEditing
Now when the user hits the next button on their phone, the function passed to onSubmitEditing will call the handleFocus function. This enables each input to focus in sequential order.
The first input is automatically focused and doesn’t need to be focused programmatically
useRef “gotcha”
In order to pass a ref instance to a child component, like we did in our AppTextInput component, the child component must be wrapped in the forwardRef function. Failure to do so may result in the following message:
"Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?"
If you’re curious why we need to use the forwardRef function to pass refs down to children components, I encourage you to check out this article by Angela Wang. There you will learn the following…
…by default React does not let a component access the DOM nodes of other components.
Here’s a working example of how to use forwardRef in a TextInput component
A few things to note before we wrap up. First, notice how the forwardRef function is responsible for our typing. It expects two type parameters including a RefType, and a PropsType. The RefType tells typescript which component it should permit to receive the forwarded ref.. The PropsType parameter tells typescript what props this component will use.
Order and number of parameters matter
If we flip flop our expected parameters as demonstrated below, we will encounter an error message.
And if we try to add a parameter to our forwardRef component, we will receive an error.
This wraps up today’s lesson! If you found value from this article please subscribe below. 👇🏾
Ways to support my newsletter
🖥 If you believe this content is valuable, share it on social media
🎤 If you believe I would be a good fit for a podcast, let your favorite podcaster know
Connect with me
Connect with me on LinkedIn!
Connect with me on Twitter!