Using the useRef hook in React
What is the useRef hook and how can we use it in React to solve some use cases? In this article, we will look at a couple of example usages of the useRef hook with JavaScript and TypeScript.
The useRef hook is one of the handy React hooks that we can use to solve some of the problems that other hooks will not solve for us. Let's have a look at what are the use cases that useRef
hook can help us with.
useRef in React: Two main use cases
- grabbing a reference to DOM element to manipulate it as in vanilla JavaScript
- creating variable "container" that behaves like a mixture of plain variable in component and variable handled by
useState
Read this article for more info about useState
You can have a look at the finished code here:
DOM Element Reference and useRef hook
The best way to wrap our heads around these is to look at some code. Here is a basic implementation of the useRef
hook grabbing DOM element ref:
We will also look at how to work with useRef and TypeScript in the bonus section of this article.
import {useRef} from 'react'
export default function BasicUseRef() {
const spanElement = useRef()
const changeText = () => {
spanElement.current.innerText = 'Hello World 🌍'
}
return (
<div>
<button onClick={changeText}>Change Text</button>
<span ref={spanElement}>Hi 👋</span>
</div>
)
}
After we import useRef
from React, we instantiate our spanElement
constant that will point to the actual span element. changeText
is a function bound to onClick
event on our button below in the JSX.
In JSX we are rendering two elements, a button and a span. We grab the reference to the span element with ref={spanElement}
and notice the initial text: Hi 👋
. Thanks to the reference, we can access native properties and methods (if the element has some) of this DOM element, in our case span
, which has property innerText
.
When we click on the button, the changeText
function fires and changes the text of the span. You might notice we are accessing the innerText
through this current
property. That is what the useRef
hook gives us back, object with current
property.
Thing to mention is, that changing the text of the span will not re-render our component. Give it a try, put a console.log right below the const spanElement.
useRef hook as an Instance variable
Now we will have a look at the second use case for useRef
hook.
import {useRef, useState} from 'react'
export default function Variables() {
let vanillaVariable = 'Vanilla' // bad and useless ❌
const [useStateVariable, setUseStateVariable] = useState('Chocolate')
const useRefVariable = useRef('Strawberry')
const changeFlavor = () => {
vanillaVariable = 'New Vanilla'
setUseStateVariable('New Chocolate')
useRefVariable.current = 'New Strawberry'
}
return (
<div>
<p><b>vanilla</b> Variable: {vanillaVariable}</p>
<p><b>useState</b> Variable: {useStateVariable}</p>
<p><b>useRef</b> Variable: {useRefVariable.current}</p>
<button onClick={changeFlavor}>New Flavor</button>
</div>
)
}
We are declaring variables in three different ways. The first one is a regular JavaScript variable in our component scope, the second is by using useState
hook and the last one is by useRef
hook.
After we click on the button we want to change our variables to have New
prepended to the original text.
Okay, this may be a really simple and dumb example but I want to point out some obvious things that can catch beginners off guard. Especially the first vanillaVariable
. Due to how React handles rendering and subsequent re-renders, this variable will never a render New Vanilla
text. Simply said, after React re-renders this component, our vanillaVariable
will be instantiated again to the Vanilla
string. This simple concept is important to understand.
Variables with useState hook
When we click on the button, our useState with TypeScript variable will change to the new text. We see the change on screen thanks to the re-render that React runs after this variable changes and only because of this reason.
Variables with useRef hook
If we look at our useRefVariable
, it also changes in the browser but that is misleading. It only happens because we are changing the useStateVariable
which causes the re-render. If we remove that setUseStateVariable
call from the changeFlavor
function, the browser UI will not re-render, hence our useRef variable will not change.
And these are the big differences between these three variables.
useRef
will not cause re-render, as our plain JavaScript variable definition but it will hold the new value after re-render, just as oursetState
variable.
Bonus: useRef and TypeScript examples
The first example is a simple one but there are some heads ups that we need to know, not to get confused when using TypeScript with React hooks.
Why should you use TypeScript instead of JavaScript?
import {useEffect, useRef} from 'react'
export default function App() {
const spanRef = useRef<HTMLSpanElement>(null)
useEffect(() => {
if (spanRef.current) {
spanRef.current.innerText = "Hello World!"
}
}, [])
return (
<div>
<span ref={spanRef}>Hello</span>
</div>
)
}
There are two things to point out and these are the basics that we need to understand when working with TypeScript. Type declarations and type checking. Once we handle these two, TypeScript is going to give us a ton of benefits.
On the fourth line, we are declaring what type will our spanRef
be. And you might be asking, how did I know it's gonna be HTMLSpanElement
? Well, I did not just guess it, I have some experience on how to figure these types out. It can get a bit frustrating when you are just starting with TypeScript, but with some practice and experience, it will get simpler.
Have a look at this link to Microsoft website where they list basic DOM Element types.
Looking at line 7, that is where we do our type checking, making sure that our spanRef.current
is defined.
useRef hook to bind events
Now we will look at the last example:
import {useRef} from 'react'
export default function UseRefElement() {
const elementRef = useRef<HTMLButtonElement>(null)
const handleClick = () => {
if (elementRef.current) {
elementRef.current.onclick = () => console.log('doing something')
}
}
return (
<div>
<button onClick={handleClick}>Bind button</button>
<button ref={elementRef}>Ref Button</button>
</div>
)
}
This is also a silly example, but it brings home the point of what useRef
hook can do. What we do here is, basically, we bind one onClick
event by clicking another button.
Notice that on line 8 we are accessing current.onclick
property of Button DOM element. That is why the syntax of onclick is not a camel case as we are used to in React. The useRef
hook gives us control over the whole DOM element.
Common React interview question: Input Focus
Last example is also quite common React.js interview question. Question is, how would you focus a specific input element in React? Let's see:
import {useEffect, useRef} from 'react'
export default function FocusInput() {
const inputRef = useRef<HTMLInputElement>(null)
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus()
}
})
return (
<div>
<input type="text"/>
<input type="text"/>
<input ref={inputRef} type="text"/>
<input type="text"/>
</div>
)
}
Again, this is simple if you know the useRef
hook. We have 4 inputs in our JSX and let's say we want to focus the third one on the page load. All we need to do is grab a reference to the input that we want to control and call the native function focus()
on the current
object that useRef
provides for us.
These were some very simple examples of useRef
hook usage with TypeScript, if you would like me to write more complex TypeScript usage with React.js hooks, let me know in the comments below.
Thank you for reading.
Keep learning and see you in the next one!