React Hooks Series: useRef
Tags: react, javascript, webdev, beginners
Published: 2020-08-20 18:58:46

## Introduction

{% codesandbox l7vuy %}

Make sure to check out my [Timer CodeSandbox](https://codesandbox.io/s/timer-clone-l7vuy?file=/src/Timer.js) first. Play around with the timer, fork the sandbox, examine the code, and even refactor to make it better!

The previous two articles in my React Hooks Series broke down useState and useEffect. This post will focus on useRef, one of my favorite hooks. I readily admit that I am not a useRef expert by any means, and this article only covers how I implement the useRef hook in relation to my Timer example. 

## A Quick Detour

Let's discuss WHY I need the useRef hook in my Timer app.

It has to do with the `PAUSE` button and how it behaves. Initially I did not have useRef tied to my pause functionality. When the user tried to pause, there was often a delay and the timer would still tick down an additional second. 

We should look at that specific behavior, because we can gain better understanding of useEffect and setTimeout also.

As a reminder, I conditionally render the `PAUSE` button when both `start === true` AND `counter` does not equal exactly `0`. 

```javascript
{
   start === true && counter !== 0
   ? 
   <button style={{fontSize: "1.5rem"}} onClick={handlePause}>PAUSE</button> 
   : 
   null 
}
```

In other words, while the timer is running, the pause button is rendered. 

```javascript
const handlePause = () => {
    setStart(false)
}
```

As you can see, `handlePause` sets `start` to `false` which makes our pause button disappear (null is rendered) and our start button is rendered in its place.

The state of `start` has changed from true to false, triggering our first useEffect (remember to ignore `pauseTimer.current` for now):

```javascript
 useEffect(() => {
      if (start === true) {
        pauseTimer.current = counter > 0 && setTimeout(() => setCounter(counter - 1), 1000)
      }
      return () => {
        clearTimeout(pauseTimer.current)
      }
  }, [start, counter, setCounter])
```
When the user hits `PAUSE`, useEffect checks to see if `start === true` (which it doesn't anymore) but the setTimeout from the previous render is still running until our useEffect determines that in fact `start` does NOT equal `true` will not run another setTimeout. But the delay happens because the prior setTimeout will complete its run. By then it is often too late and another second has passed. 

Want to see this behavior in action? Open the [Timer CodeSandbox](https://codesandbox.io/s/timer-clone-l7vuy?file=/src/Timer.js)  and delete `pauseTimer.current = ` from line 19, run the timer and try to pause it a few times. You will notice the timer not pausing immediately.

Now that we understand the problem, we can fix it!

Enter the useRef hook to save the day!

# Part Three - useRef

Understanding useRef might take some time. I know it did for me. First let's see what the [React docs](https://reactjs.org/docs/hooks-reference.html#useref) have to say:
>useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.

Okay, say what? 

If you are not sure what any of that means, you are not alone! 

I found this blog post written by [Lee Warrick](https://leewarrick.com/blog/react-use-effect-explained/) very helpful, particularly his explanation for useRef:

>Refs exist outside of the re-render cycle.
Think of refs as a variable you’re setting to the side. When your component re-runs it happily skips over that ref until you call it somewhere with .current.

![Lightbulb lighting up over man's head](https://dev-to-uploads.s3.amazonaws.com/i/s5nwb2swkadm1k0c14ge.gif)

That was my lightbulb moment. A ref is a variable you can define based on an object in state, which will not be affected even when state changes. It will hold its value until you tell it to do something else!

Let's see it in action in our Timer app.

Add useRef to our React import:
```javascript
import React, { useState, useEffect, useRef } from "react";
```

From the docs:

>```javascript
const refContainer = useRef(initialValue);
```
Defining an instance of an object to "reference" later.

Ours looks like:

```javascript
const pauseTimer = useRef(null)
```

Make sure to give it a meaningful name, especially if you're using multiple useRefs. Mine is `pauseTimer` because that is what I want it to do when called. `null` is my intial value inside `useRef()` because it doesn't really matter what the initial state of `pauseTimer` is in my function. We only care what the reference to pauseTimer is once the timer starts ticking down.

`pauseTimer` is an object with a property of `current`. EVERY ref created by useRef will be an object with a property of `current`. `pauseTimer.current` will be a value which we can set.

Let's take a look at our useEffect one more time, now paying special attention to `pauseTimer.current`. Here we are setting our conditional (is `counter` greater than `0`?) setTimeout as the value to `pauseTimer.current`. This gives us access to the value of setTimeout anywhere! 

```javascript
useEffect(() => {
   if (start === true) {
     pauseTimer.current = counter > 0 && setTimeout(() => 
   setCounter(counter - 1), 1000)
   }
   return () => {
     clearTimeout(pauseTimer.current)
   }
}, [start, counter, setCounter])
```

From here it's pretty straight forward. When the user selects `PAUSE` now, `start` updates to `false` and the useEffect can't run the setTimeout so it runs the clean-up function:

```javascript
return () => {
     clearTimeout(pauseTimer.current)
}
```

If we didn't have `pauseTimer.current` inside our clearTimeout, the timer would continue to tick for another second, just as before because our setTimeout inside the conditional block `if (start === true)` will run its full course even if we set `start` to `false` a second before.

BUT! Since we have `pauseTimer.current` (a reference to our current setTimeout value) inside clearTimeout, useEffect will skip over `if (start === true)` and immediately run its cleanup function and stop our setTimeout in its tracks!

And that's the power of useRef! Ability to access a reference to a value anywhere (you can even pass them down from parent to child!) and those references won't change until you tell it to (like we do with our timer every second it updates).

## Bonus 

This is just the tip of the useRef iceberg. You might be more familiar with useRef and interacting with DOM elements. 

In my [portfolio website](https://www.jamescox.dev/), useRef dictates how I open and close my animated navigation screen.

Inside my component function SideNavBar:

I define my ref
```javascript
const navRef = useRef()
```
Create functions to close and open the navigation

```javascript
function openNav() {
    navRef.current.style.width = "100%"
}

function closeNav() {
    navRef.current.style.width = "0%"
}
```

And set the React `ref` attribute of `div` to `navRef`

```javascript
<div id="mySidenav" className="sidenav" ref={navRef}>
```
And my CSS file with the `sidenav` class

```css
.sidenav {
  height: 100%;
  width: 0;
  position: fixed;
  z-index: 2;
  top: 0;
  left: 0;
  background-color: #212121;
  overflow-x: hidden;
  transition: 0.6s;
  padding-top: 5rem;
}
```
Pretty cool, right?

`navRef` interacts with the DOM element `div className="sidenav"` because it has the attribute `ref={navRef}` and when `openNav()` is called, `navRef.current.style.width` gets updated to `"100%"`.

And vice versa when 'closeNav()' is called. 

## Wrapping up

I hope you enjoyed reading the third installment in my React Hooks Series! If you've made it this far, first

![Ron Swanson is really proud of you gif](https://dev-to-uploads.s3.amazonaws.com/i/7ssa7m8m4zq4d99ux32y.gif)

and second 

![Fez is surfing, thumbs up and thank you!](https://dev-to-uploads.s3.amazonaws.com/i/r4n14e38ii986c11oapg.gif)

I plan to continue this series on React hooks. I might cover different aspects of the same hooks or explore all new hooks. So stay tuned and as always, thank you again. It really means so much to me that ANYONE would read anything I write.

Please leave comments, feedback or corrections. I am SURE that I missed something or maybe explained concepts incorrectly. If you see something, let me know! I am doing this to learn myself. 

Until next time...

HAPPY CODING