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!
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?
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.
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!
Top comments (24)
I love how you explain things in such a clear way. Thanks!
Thanks David! :)
Great illustrations and awesome explanation. Thanks, Keep it up!
Thank you for reading and the words of encouragement Carlos!
Milu, I love this article
Thank you!!! :)
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.
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!
Illustrations, pictures, clear examples. Valuable content. Thanks so much for sharing that! It is my new favorite series about Hooks.
Thank you Diego! I really appreciate your feedback :)
This was awesome! I'll pass it along to some learners I know! Great work!!
Thank you for the kind words and for sharing this post Jacob!
Yeah!!! Besides being great info on React, this is the level of quality we should all be striving for in our technical articles! 🔥
Thank you Felix! This comment encourages me to continue sharing content :)
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 betestRef.current + 1
? I believetestRef
is a plain object, so there's no need to settestRef
equal to a number; it's enough to update thecurrent
property.Great work! Crystal clear explanation!
Thank you! :)
I always struggled with hooks other than useState and useEffect. Thank you for explaining it elegantly
Thank you Usman! :)