useRef( ) : DOM and beyond...
Tags: react, javascript, webdev
Published: 2021-04-28 05:46:49

This blog assumes that you know the React fundamentals and `useState` hook.

>[Read my article on what is a state in an App? and useState hook](https://shubhs.hashnode.dev/usestate-like-a-pro) (there's a TL;DR section, if you want to have a quick look 😉), you would need some of that state-related concept in this blog.

## What is `useRef`?

>Ref is just a `{ current: initialValue }` object. It's nothing special. Both `useRef(initivalValue)` and `createRef()` give you that. - [Dan Abramov](https://twitter.com/dan_abramov/status/1099840977601261568?s=20) 

[![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1613219236244/hcyPQrsaQ.png)](https://twitter.com/dan_abramov/status/1099842565631819776?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1099842565631819776%7Ctwgr%5E%7Ctwcon%5Es1_c10&ref_url=https%3A%2F%2Fpublish.twitter.com%2F%3Fquery%3Dhttps3A2F2Ftwitter.com2Fdan_abramov2Fstatus2F1099842565631819776widget%3DTweet)

```javascript
function useRef(initialValue) {
  // useRef works more like this
  return React.useState({
    current: initialValue
  })[0]
}
```
> You just created your own `useRef`. 🤯🤯🤯

## Why do we need `useRef`?
`useRef` actually serves two purposes, 
  *  Provides a reference to the DOM elements
  *  Returns mutable value which persists across renders

### But, what is this mutable and persistent value?

Persistent value is the kind of value that stays persistent between __renders__, that's what `useState` returns, a persistent value (`state`) and updater API (`setState`) to update that state which causes a re-render for that component. for an application to update its View(UI) you need that setState API.

### But what if you want to have a value that stays persistent and does not cause a re-render of a Component.🤔

This is such a fundamental need that react provides a built-in API for it, That's what __useRef__ is for.
<hr>

## useRef
```javascript
// you can set any type of data as initialValue same as useState()
const objectWithCurrentProperty = React.useRef(initialValue)

const refOne = React.useRef() // returns {current : undefined}
const refTwo = React.useRef(1) // returns {current : 1}
const refThree = React.useRef([]) //returns {current : []}
```
`useRef` takes the initial value as an argument for the returned value.
These return values would be persisted and you can also mutate them according to your need.

## Accessing the DOM with useRef

There are some libraries (e.g. Three.js or Anime.js) that need access to the dom.
when we write jsx it gets converted into React.createElement. a `<div>Hello World</div>` which we write as jsx gets converted into `React.createElement("div", null, "Hello World")` so you don't have any direct access to the DOM nodes from your returned jsx.

So to get access to the DOM, you need to ask React to give you access to a particular DOM node when it renders your component. The way this happens is through a special prop called `ref`.


```javascript
function UploadButton({ handleUpload }) {
  const inputRef = React.useRef();

  const handleClick = () => inputRef.current.click();

  return (
    <>
      <input type="file" hidden ref={inputRef} onInput={handleUpload} />
      <button onClick={handleClick}>Upload</button>
    </>
  );
}
```
In this example, we are passing the `ref` prop `inputRef` to the _hidden file_ input, and when we click on a button that uses `inputRef.current` to get access to that __DOM element__ of that input and on that element we are calling `click` event. 

some other cases would be like getting a value from an input, changing focus, or selecting text. 

>react-hook-form is a form library that encourages [uncontrolled](https://reactjs.org/docs/uncontrolled-components.html) way of handling forms and `ref` prop to handle forms in react component, give it a try it's awesome.

## Mutable Data Storage

Before we try to understand what this is and why do we need this? If you can, I would suggest you [create](https://react.new) a stopwatch component with stop and resume functionality.
<br>. <br>. <br>. <br>
Spoilers ahead....
<br>. <br> .<br> .<br>

```js
// Here is a simple StopWatch component which updates time every 1 second
function StopWatch() {
  const [time, setTime] = useState(0);

  useEffect(() => {
      const interval = setInterval(() => {
        setTime((s) => s + 1);
      }, 1000);
      
      // clearInterval before unmounting component 
      return () => clearInterval(interval);
  }, []);

  return (<div>{time}</div>);
}
```

But now we need a button which will make the ticking of time stop and resume, for that we would add ticking state and update our useEffect.
```js
function StopWatch() {
  const [time, setTime] = useState(0);
  const [ticking, setTicking] = useState(false);

  useEffect(() => {
    if (ticking) {
      const interval = setInterval(() => {
        setTime((ms) => ms + 1)
      }, 1000);
      return () => clearInterval(interval);
    } else {
      // 🤔 but we don't have access "interval" here
      clearInterval(interval)
    }
  }, [ticking]);

  return (
    <div>
      <div>{time}</div>
      <button onClick={() => setTicking(c => !c)}>{ticking ? 'Pause' : 'Resume'}</button>
    </div>
  )
}
```

So where do we put our interval now? if you put this outside `useEffect` on every render all local variables would reset and it would become undefined again

```js
function StopWatch() {
 ...
// I 👇 would keep becoming undefined on every re-render 😢
let interval;

useEffect ...
}
```

So now we want something that stays persistent across renders and doesn't cause re-renders, and you guessed it right, we need useRef here.

```js
function StopWatch() {
  const [time, setTime] = useState(0)
  const [ticking, setTicking] = useState(false)
  // mutable and persistant 
  const interval = useRef()

  useEffect(() => {
    if (ticking) {
      // `interval` would not reset when component re-renders
      interval.current = setInterval(() => {
        setTime((ms) => ms + 1)
      }, 1000)
      return () => clearInterval(interval.current)
    } else {
      // and now you have access to the interval
      interval.current && clearInterval(interval.current)
    }
  }, [ticking])

  return (
    <div className="App">
      <h1>{time}</h1>
      <button onClick={() => setTicking(c => !c)}>
        {time === 0 ? 'Start' : ticking ? 'Pause' : 'Resume'}
      </button>
    </div>
  )
}
```

Where to go from here? from now you can go and read more about [forwarding refs](https://reactjs.org/docs/forwarding-refs.html) and [useImperativeHandle](https://reactjs.org/docs/hooks-reference.html#useimperativehandle). Thanks for reading.