Hooks introduced in React 16.8, allow us to use stateful logic, lifecycle methods, and side-effects in Functional Components. React's official doc on Hooks is the best way to get you started with them.
useRef is one such very helpful Hook that returns a
mutable ref object having
current property initialized with the value passed as the argument. This mutable object's value persists across multiple renders in the lifecycle of the component and can have various applications based on use-cases. In this blog, we'll understand different scenarios in which the
useRef Hook can be helpful with the aid of examples.
We'll look at following 3 applications of
useRef Hook with examples:
- Remembering previous value of a state variable
- Fixing closure issue while using intervals
- Referencing a DOM element
1. Remembering previous prop value:
Sometimes, we may want to know when the prop variable is updated and perform some operations based on its previous value. In class-based components, we have a lifecycle method
componentDidUpdate which helps us in this scenario. But in Hooks, we have no direct way to know previous prop values. Using the mutable object returned by the
useRef Hook, we can achieve this. Let's understand by creating a simple example:
Here, we have a textbox and a
div element will render the previous prop value
prevVal of the state variable
name passed to it. We have abstracted the logic to return the previous prop value in a Hook called
usePrev. Let's understand this line by line after
Main, called for rendering by
Line 5: state variable
namewith initial value
usePrevHook called with
nameinitial value passed as argument. Now, inside this Hook,
useRefHook returns a mutable object which is then assigned to
Line 30: Next, return value of
usePrevis called, and it returns
usePrevHook is not yet called. It will be called only after the component calling
usePrevis rendered. Thus,
undefinedis returned to
- Line 25:
Line 7: The control now returns to the
Main, and it is rendered.
Line 27-29: After
usePrevis called, which assigns value of
Next time if we change the value of the textbox to say
a, the same flow will be triggered i.e. the
prevValue will be rendered as
"" which was the previous value of
name. After re-rendering of
usePrev will re-assign the value of
ref value is updated only when its
current is explicitly assigned some value. This can also act as an instance variable of a class where some values need to be persisted/updated without causing overhead of re-rendering as in the case of state variables.
2. Working with setInterval:
Using intervals with Hooks becomes a bit tricky. We'll look at different approaches, problems we encounter and find out how
useRef helps in solving them.
Let's understand by creating a simple timer that counts from 99 to 1.
Here, we have a
count variable initialized to 99. We initialize the interval on the mount in
useEffect and clean it up on unmount.
After an interval of every 1000 ms, we are decrementing the value of
count. However, on running this, we observe that
count only decrements by 1 to 98 and remains as it is. This happens because
useEffect runs only on the mount and remembers the value of
count which is 99. The
setInterval closes over this value and runs
setCount(count - 1) and decrements
count to 98. Since effect runs only once the closure is not updated and
setInterval even though keeps running on every interval doesn't update the value and
count is stuck at 98.
We can fix this issue by removing the dependency array, thus calling
useEffect on every update of
count. But this will create a new interval on every update which is not desirable. You can check this by logging interval id in the callback.
The better approach would be as follows:
- Line 6: We create a mutable object with
useRefand assign it to
- Line 8: We create a function
callbackthat decrements the
countuntil it is greater than 0.
- Line 15 - 17: We assign this
useEffectis called on every update of
countbecause a new instance of
callbackis created on every update whose reference is assigned to
- Line 19 - 24: We call
useEffecton the mount to create an interval and call
callbackFnon every interval.
Observe that we have created only one interval on the mount. The
callback's reference is assigned to ref
callbackFn on every re-render. This way we also fixed the closure problem we came across earlier, as a new instance of
callback is being assigned to
callbackFn on every re-render and
callbackFn having an updated instance is called on every interval of one second.
3. Reference to a DOM variable:
This is the most common usage of
useRef. Let's change the color of the counter to red when the mouse hovers on it and revert when the mouse leaves. We'll also pause the counter when the mouse enters and resume when it leaves. The code is as follows:
- Line 7: We create a ref variable
- Line 8: We create a state variable
isRunningto toggle the running status of the counter. By default, it is set to
- Line 9: We also create a state variable
delaywhich will be used as interval delay in
setInterval. By default, it is set to
- Line 41: We assign
h1element which is rendering the
- Line 42-45:
h1, we update its color to red using its ref
counterRef. We also pause the counter by setting
- Line 46-49:
h1, we update its color to black using its ref
counterRefand resume the counter by setting
- Line 31-37: this
useEffectis called on the update of
isRunningis set to true, it sets
null. Else, it sets
- Line 22-29: The
useEffectused for setting the interval is now updated with the dependency of
null, that is
onMouseEnterof the counter, the interval creation block is skipped and counter is paused.
onMouseLeaveof the counter, the
delayis set to
1000, the interval is set and flow continues from where it stopped.
These are the practical usage of
useRef Hook in the React application. I hope this will help you to make the best use of
useRef Hook in your React application.
Thank you for reading!