DEV Community

Cover image for Demystifying React Hooks: useRef
Milu
Milu

Posted on • Updated on

Demystifying React Hooks: useRef

React Hooks changed the game when they came out! They are a simpler way to encapsulate stateful behavior and side effects in a user interface while using less code and increasing readability. Some hooks are easier to understand and use than others, this is why this series of posts will focus on demystifying the hooks that are not as straightforward.

Last week, we explored useCallback and useMemo in depth. Today, let’s start by explaining what it means to imperatively modify a child and the DOM, continue by focusing on defining the useRef hook and discussing how, when, and where to use it.

Imperatively modify a child and the DOM

If you have worked with React before, you must be familiar with how a parent component uses props to interact with its children. In order to re-render a child component with different data, the parent component passes new props.

At times, we face situations where we need to imperatively modify a child component outside of the typical "passing props" dataflow or we want to manually access DOM nodes or React elements created in the render method. Here is when useRef() becomes handy!

Alt Text

Why is useRef() Hook special?

The useRef() hook persists in between component renders (like state).

The magic of useRef is that it can be mutated without causing your component to re-update because the useRef value exists outside of the render cycle.

How to use useRef() Hook?

You initialize a useRef() hook by passing an initial value to it or initializing it empty and updating its value later:

const testRef = useRef(1)

useRef() stores an object that contains an attribute called current, which stores the passed value, in our example, it would hold the value 1.

testRef = { current: 1 }

When to use useRef() Hook?

To manage focus, text selection, or media playback. Most elements inside of your document have a ref attribute, which facilitates the use of useRef to reference elements inside of your HTML. As an example, take a look at this <input/> HTML tag, we created a useRef value and passed it to <input/> as a ref attribute. Now, we are able to imperatively modify the input element with a couple of functions that get the <input/> to focus or blur.

To access a previous value. Another helpful use for useRef is to store the previous value of your state. Take a look at the example below, we have a list of three Pokemon and you need to select your favorite. Currently Pikachu is selected… but let’s be real, Pikachu is overrated. Select any of the other options and you will see your previous selection at the bottom. This is possible as a result of the use of our useRef hook:

const previousSelected = useRef()

Then, everytime we select a different option, we keep track of the previous selection in our changeSelection() function:

previousSelected.current = favPokemon

Also, according to React Docs, refs are useful to trigger imperative animations and to integrate with third-party DOM libraries.

Where should you update a useRef() value?

Alt Text

Updating a ref value is considered a side effect. This is the reason why you want to update your ref value in event handlers and effects and not during rendering (unless you are working on lazy initialization). React docs warns us that not following this rule could lead to unexpected behaviors.

Should you use refs instead of state?

Big NO. Refs aren’t reactive, which means changing its value won’t cause the HTML to update.

Take a look at the following example to make sure you understand why refs should not replace state.

We initialized a state and a ref with $1000 dollars. This component allows you to spend this value dollar-by-dollar every time you click the button Spend.

When you spend the money value stored in state, it triggers a re-render and it updates the view to show you the new value.

Now, if you spend the money value stored in ref, it will also subtract a dollar on every click, however, this change won’t trigger a re-render so you won’t see a change in your HTML.

You can check out the console to see that ref value is truly changing inside the component.

Alt Text

Is useRef() the same as createRef?

No.

createRef() useful to access DOM nodes or React elements. BUT it creates a new instance of the ref on every render instead of keeping a value between renders when used in functional components (This does not apply if you are using a class component!).

useRef() useful to access DOM nodes or React elements AND it holds a value even when a component re-renders. Here is an example that will help you see the difference.

Take a look at the following code, we are initializing two ref values as null, using createRef and the useRef respectively.

Everytime we click on the Add a render! button, we update the renderCounter state triggering a re-render. On every render, we check if the refs values are null and if they are, we assign the current renderCounter state value to it.

Notice that the ref value created using useRef is only null on the first render, so it is set to 1 once and never again.

On the other hand, the ref value created using createRef is created on every single render, so it always starts as null and then it is reassigned the current state value under renderCounter.

Side note: This is a simple example to illustrate the difference in between createRef and useRef. However, modifying the value of a ref should occur in an effect or event handler, not as shown in this example.

Summary

The useRef() hook helps create mutable variables inside a functional component that won’t update on every render.

  • Refs are helpful to access DOM nodes or React elements (what is being rendered) and for keeping values in between renders, such as the previous value of a state.

  • useRef() should not be used to replace state because it is not reactive and it won’t trigger a re-render.

  • Refs should be updated inside effects and event handlers to avoid weird behaviors.


I hope this post helped you expand your understanding of useRef() and that you will start taking advantage of this feature in your future projects.

I post new content every week. We will be exploring a different React hook next Weekend!

Follow me on Twitter and Dev.to to keep up with new posts!

Top comments (24)

Collapse
 
davidyaonz profile image
David Yao

I love how you explain things in such a clear way. Thanks!

Collapse
 
milu_franz profile image
Milu

Thanks David! :)

Collapse
 
caroso1222 profile image
Carlos Roso

Great illustrations and awesome explanation. Thanks, Keep it up!

Collapse
 
milu_franz profile image
Milu

Thank you for reading and the words of encouragement Carlos!

Collapse
 
ponyjackal profile image
ponyjackal

Milu, I love this article

Collapse
 
milu_franz profile image
Milu

Thank you!!! :)

Collapse
 
valainisgt profile image
Greg Valainis

Great article on the underpinnings and usage of useRef. Your examples and illustrations do a great job of showing practical usage. However, your discussion about createRef is somewhat misleading. createRef "creates a new instance of the ref on every render instead of keeping a value between renders" is only true within a function component. createRef should not be used in function components. It should be used in class components where a new instance is not created on every render.

Collapse
 
milu_franz profile image
Milu

Thank you for pointing this out Greg! I haven't used class components since React hooks came out and I shouldn't have assumed everybody is on the same boat as me. I've updated my post to clarify the use of createRef. Thanks again for the feedback!

Collapse
 
diazevedoo profile image
Diego Azevedo

Illustrations, pictures, clear examples. Valuable content. Thanks so much for sharing that! It is my new favorite series about Hooks.

Collapse
 
milu_franz profile image
Milu

Thank you Diego! I really appreciate your feedback :)

Collapse
 
jacobmgevans profile image
Jacob Evans

This was awesome! I'll pass it along to some learners I know! Great work!!

Collapse
 
milu_franz profile image
Milu

Thank you for the kind words and for sharing this post Jacob!

Collapse
 
terkwood profile image
Felix Terkhorn

Yeah!!! Besides being great info on React, this is the level of quality we should all be striving for in our technical articles! 🔥

Collapse
 
milu_franz profile image
Milu

Thank you Felix! This comment encourages me to continue sharing content :)

Collapse
 
travelintervals profile image
Euphoric

Thanks for this article and the codepen examples!
One question, in your "Where should you update a useRef() value?" example, you have testRef = testRef.current + 1. Can it just be testRef.current + 1? I believe testRef is a plain object, so there's no need to set testRef equal to a number; it's enough to update the current property.

Collapse
 
emmanuelkaranja profile image
emmanuel-karanja

Great work! Crystal clear explanation!

Collapse
 
milu_franz profile image
Milu

Thank you! :)

Collapse
 
monfernape profile image
Usman Khalil

I always struggled with hooks other than useState and useEffect. Thank you for explaining it elegantly

Collapse
 
milu_franz profile image
Milu

Thank you Usman! :)