Photo by Lautaro Andreani on Unsplash
Day 43-useRef Hook in practice
Hi and welcome to day 43 of my 100days of code challenge!ππ
Refs to select DOM elements
How not to select DOM elementsβ
Here's an example of how you might attempt to automatically focus on the input field using traditional DOM manipulation within a useEffect
hook:
This π useEffect
hook is intended to run once when the component mounts, as indicated by the empty dependency array []
. Then I selected the input field with the class name "search" using document.querySelector()
. It searches for the first element in the document that matches the specified CSS selector ".search".
Once the input field is selected, el.focus()
is called in an attempt to bring focus to this element, making it ready for user input.
Now, upon loading the application, you'll notice that the input field automatically gains focus, indicating that our code is functioning as intended. However, this approach of manually selecting DOM elements isn't aligned with the typical React methodology, which emphasizes a declarative approach. In other words, using document.querySelector()
and directly manipulating the DOM goes against the principles of React. React encourages a declarative approach where you describe how your UI should look based on the application's state, rather than imperatively manipulating the DOM.
To address these concerns and maintain consistency with React's principles, we can utilize the concept of refs. Refs allow us to reference DOM elements in a more declarative manner, in line with React's paradigm.
How to select DOM elements
Implementing a ref for a DOM element involves three steps. Firstly, we create the ref using the useRef
hook, providing an initial value for its current
property. Typically, when working with a DOM element, the initial value is set to null
.ππ
As a second step, let's go to the element we want to select. We use the ref
prop and assign it the inputEl
we created earlier. This links the element and the ref directly in a declarative manner. So, there's no need for manual query selections like before.ππ
To use this ref in the third step, we'll use the useEffect
hook again. It runs a function on mount, focusing on the input element. The line inputEl.current.focus()
accesses the input element's DOM node via the current
property of the ref and calls the focus()
method, bringing it into focus.ππ
We need to use an effect to work with a ref that holds a DOM element like this because the ref is added to the DOM element after the DOM loads. So, we can only access it in an effect, which runs after the DOM is loaded. This is the best way to handle a ref with a DOM element.ππ
Refs to persist data between renders
Imagine we're building a movie rating application, and behind the scenes, we want to track how many times a user selects a different rating for a movie without displaying this count on the screen. And so this time what we need is a variable that is persisted between renders but that does not cause a re-render when it is updated.We'll use a ref for this purpose.
First, let's create a ref called countRef
to store the count inside the MovieDetails
component. We'll initialize it with a value of zero.ππ
We'll use a useEffect
hook to update the ref
when the user rates a movie because we cannot directly alter the ref
during rendering- so we are not allowed to mutate the ref
in render logic. Additionally, we'll ensure the count only increments when there's an existing rating, so we don't increase when the component first mounts.π
Finally, when the user adds the movie to their list, we'll get the count from the ref and put it in the new watched movie details. Now, let's look at the function where the new movie is added to the watch list.ππ
To see if this change worked, let's go to our app's state. We'll check there to make sure the value was set correctly because we're not showing this new value anywhere on the screen right now. Here's where you can find it.ππ
So, every time the user updated their rating, it caused the component to re-render. After this re-render, the effect we set up was executed, meaning that after the rating changed, our reference (ref
) would also be updated. We accomplished this by incrementing the current
property by one.
ref
), which persists across renders but doesn't trigger a re-render when updated. This is why we typically avoid using a ref in the JSX output.