Things you need to know about React ref
Tags: react, javascript, tutorial, beginners
Published: 2022-03-09 16:03:58

In my [previous article](https://dev.to/romaintrotard/things-you-need-to-know-about-react-state-2l8o), I talked about **React state**. Now it's time to discuss about **React reference**:
- What is it?
- How to use them?
- When to use it?
- How does it work under the hood?

Let's go.

----

## What is it?

A **React reference** is simply an object that has it's reference which is fixed during component renders and that that a key `current` that is mutated.

Unlike **React state**, when we change a reference (mutates it) React **WILL NOT** trigger a re-render of the component.

----

## How to use it?

Before version `16.8.6` of React it was possible only to use ref on **class component**.

### Class component

To create a reference in a **Class component** you only have to call:

```js 
import React from 'react';

const ref = React.createRef();
```

Call it in:

- the constructor:

```jsx
class MyClassComponent extends React.Component {
  constructor() {
    this.myRef = React.createRef();
  }

  render() {
    return <p>A simple class component with a ref</p>;
  }
}
```


- directly declaring the property name you want:

```jsx
class MyClassComponent extends React.Component {
  myRef = React.createRef();

  render() {
    return <p>A simple class component with a state</p>;
  }
}
```

> Note: You can use both as you wish. It's the same. But constructor will give you the possibility to use props to initialize the ref: 

```jsx
class MyClassComponent extends React.Component {
  constructor(props) {
    this.myRef = React.createRef();
    this.myRef.current = props.someValue;
  }

  render() {
    return <p>A simple class component with a ref</p>;
  }
}
```

----

### Functional component

After `16.8.6`, hooks have been introduced, especially **`useRef`**:

```js
import { useRef } from 'react';

const ref = useRef(initValue);
```

With a component you will have:

```jsx
import { useRef } from "react";

function StateFunctionalComponent() {
  // myRef will have a fixed reference
  // The initial value is 0
  const myRef = useRef(0);

  return <p>Functional component with state</p>;
}
```

----

### Access and update

Then, once you have created the reference, you probably want to get the value and update it. 
You will just work with the `current` property:

```js
const myRef = useRef();

// Get the value
console.log('The value is:', myRef.current);

// Update the value
myRef.current = 'New value';
```

> **Warning:** Do not update the reference in the render directly. You will see in the next part where to do it and when it is useful.

----

## What should not be done with ref?

I spoiled it a little at the end of the previous part, you should never update/read a reference inside the render directly, the only exception is for lazy initialization.

**What is lazy initialization?**

Lazy init is when you check if the ref has not value to set one. It's useful for example when you work with **Portal** to get the container:

```jsx
function MyComponent() {
  const container = useRef();

  if (!container) {
    container.current =
      document.getElementById("myContainer");
  }

  return ReactDOM.createPortal(
    <p>Will be inside the element with id: myContainer</p>,
    container.current
  );
}
```

> **Note:** If you do not know what a **Portal** is, here is the `React` definition:

> Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.

**Why should you not update/read in render?**

It's because of incoming **concurrent rendering**. With **concurrent mode**, the rendering process will not be synchronous anymore, so it will be possible that rendering of some component is "paused" to keep as most as possible 60 frames per second and a nice interactivity feeling.
So it would be possible to make inconcistency if a ref is used inside render for UI (because we mutate an object).
Whereas `React` will ensure there is no inconcistency with **React states**.

To help you to identify where are problems with `ref`, there will be some warning in the console about that. You can see this PR: [useRef: Warn about reading or writing mutable values during render](https://github.com/facebook/react/pull/18545) that introduce the warnings.


----

## When to use it?

Okay now that we know what is it and that the component will not re-render after mutation of the reference, when is it useful?

There is multiple cases, let's see them.

> **Note:** In all the example I will code with Functional component but you can do the same with Class component

### Get reference to DOM element

The main role of reference is to have access to a DOM element and then be able to do some process on the element like: focus, get the value of an input, ...

In this case, you have to put the ref on the "React DOM element".

```jsx
function MyComponent() {
  const inputRef = useRef();

  return <input type="text" ref={inputRef} />;
}
```

Then you have access to the real **DOM element** through `ref.current`.

For example, with the input we can get the value filled by the user:

```jsx
function MyComponent() {
  const inputRef = useRef();

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button
        type="button"
        onClick={() =>
          console.log(
            "The value is:",
            inputRef.current.value
          )
        }
      >
        Show the value
      </button>
    </div>
  );
}
```

> **Note:** Get element with `useRef` if the element to select is here at the mount.

> If the element is conditionally rendered, you would probably prefer to use a **ref callback**

```jsx
function MyComponent() {
  const [show, setShow] = useState(false);

  const refCallback = useCallback((node) => {
    if (!node) {
      console.log("The node is unmounted");
    } else {
      console.log("The node is", node);
    }
  }, []);

  return (
    <div>
      <button
        type="button"
        onClick={() => setShow((prev) => !prev)}
      >
        Show / unshow
      </button>
      {show && (
        <div ref={refCallback}>
          Element with ref callback
        </div>
      )}
    </div>
  );
}
```

> **Note:** You can see that I `useCallback` the ref callback otherwise the method will called at each render that will be bad for the behavior's consistency. I you want other cases where to use `useCallback`, you can see my article [When to use useCallback](https://dev.to/romaintrotard/when-to-use-usecallback-3o6e).

> **Note:** If you want to pass the `ref` to a component you will have to choice with FC: pass it with different name than `ref` or `forwardRef` on the component. In CC you will have to name it differently.

```jsx
// Forward the ref
const FunctionalComponent = React.forwardRef(
  (props, ref) => {
    // Content of component
  }
);

// Different name
function FunctionalComponent({ customRef }) {
  // Content of component
}
```

----

### Store data not useful for UI (used in event listener for example)

One other case is to store value that does not need to trigger a re-render, for example when you use it only in event listener.

Let's take the example where you want to prevent clicking on a button (but not show a different style), in this case let's use a `ref`:

```jsx
function MyComponent() {
  const preventClick = useRef(false);

  return (
    <div>
      <button
        type="button"
        onClick={() =>
          (preventClick.current = !preventClick.current)
        }
      >
        Enable / Disable click
      </button>
      <button
        type="button"
        onClick={() => {
          if (preventClick.current) {
            return;
          }

          console.log("You are able to click");
        }}
      >
        Will you be able to click?
      </button>
    </div>
  );
}
```

> **Note:** In reality, we would probably show a different style which will make us use a `state` instead.

----

### Get latest value of a value in `useCallback`

Sometimes I do not want to `useCallback` some function for example when doing memoization for performances.

For example:

```jsx
const callback = useCallback(() => {
  console.log("I use the dep:", value);
}, [value]);
```

This callback will be recreated, each time `value` is changing. But most of the time I do not want that. For example when the callback is used as an event handler.

So in this case, I will put the `value` in a `ref` that will ensure me to get the latest value of the `value` without recreating a new callback.

```jsx
const valueRef = useRef(value);

useEffect(() => {
  // I don't care that it's executed at each render
  // because I want always the latest value
  // I save a check on the dependency
  valueRef.current = value;
});

const reallyStableCallback = useCallback(() => {
  console.log("I use the dep:", valueRef.current);
}, []);
```

> **Warning:** Do not forget to update the reference! Otherwise, you will not have the latest `value`.

----

### Count the number of render

You can easily store the number of render thanks to a `ref` combined with `useEffect`:

```jsx
function MyComponent() {
  const renderCount = useRef(1);

  useEffect(() => {
    renderCount.current++;
  });

  return <p>Number of render: {renderCount.current}</p>;
}
```

----

### Know if a component has been already mounted

```jsx
function MyComponent() {
  const isMounted = useRef(false);
  const [count, setCount] = useState(0);

  useEffect(() => {
    if (isMounted.current) {
      console.log("The count has changed to:", count);
    }
  }, [count]);

  useEffect(() => {
    isMounted.current = true;
  }, []);

  return (
    <button
      type="button"
      onClick={() => setCount((prev) => prev + 1)}
    >
      Inc count: {count}
    </button>
  );
}
```

> **Warning:** When using this pattern, to know if the component is mounted in a `useEffect` then the order of `useEffect` is really important. The one that updates the `isMounted` MUST be the last one.

----

### Keep a previous value

Another use case is when you want to keep the value of a state during the previous render. It can be useful when you compare to the current one in a `useEffect` to know if it is one of the dependency that has changed. 

```js
function MyComponent() {
  const [otherState, setOtherState] = useState(0);
  const [count, setCount] = useState(0);
  const previousCount = useRef(count);

  useEffect(() => {
    if (previousCount.current !== count) {
      console.log(
        "The count has changed during this render " +
          "(maybe otherState too)"
      );
    } else {
      console.log(
        "It's sure that otherState has changed " +
          "during this render"
      );
    }
  }, [count, otherState]);

  useEffect(() => {
    previousCount.current = count;
  }, [count]);

  return (
    <div>
      <button
        type="button"
        onClick={() => setCount((prev) => prev + 1)}
      >
        Inc count: {count}
      </button>
      <button
        type="button"
        onClick={() => setOtherState((prev) => prev + 1)}
      >
        Inc otherState: {otherState}
      </button>
      <button
        type="button"
        onClick={() => {
          setCount((prev) => prev + 1);
          setOtherState((prev) => prev + 1);
        }}
      >
        Inc both
      </button>
    </div>
  );
}
```

> **Note:** It's not because the `count` has changed during the render than the `otherState` did not changed too. It can happen because `React` batches update in that case.

----

## How `React` assigns the DOM node to a ref?

Previously we have seen than the main use case is to get a reference to a DOM node. But how does React do it under the hood?

One thing you should understand is the difference of execution between `useEffect` and `useLayoutEffect`: `layoutEffect`s are executed synchronously after the rendering phase contrary to `effect`s that are executed asynchronously (they are just schedule but not ensure to be executed directly).

At the first rendering, React will transform **React elements** into **Fiber nodes**.

Basically, during the rendering, React will process from the **Root node** until the deepest component. Then it will go up in the component tree.

![React rendering phase](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rzvk1prbxme57jo7ie2o.png)
 
> **Note:** Here I have a simple tree with no siblings component. When there are siblings it processes a branch, complete the work and go on the other branch.


**Begin work phase:**

When processing a node, from top to bottom, React can detect when a node is a **HostComponent** (i.e. `div`, `p`, ... native DOM tag) and has a prop `ref` assign to it.

If it's the case, React will flag this node and put on the fiber node a `ref` key containing the reference to the `ref` (which is basically an object with a `current` key as we have seen earlier).

**Complete work phase:**

Then, when React has reached the last child it will go up in the tree, it's at this moment that the previously setted flag has an effect. It will tell to the parent fiber node:

> _HostComponent_: "Hey, someone wants my DOM node, put me in you (fiber node) as a `firstEffect` that will be executed before all `layoutEffect` you have :)"

Then the parent fiber node tells to its parent:

> _HostComponent parent_: "Hey, we told me that this fiber node is my `firstEffect` it needs to be yours too. But I have some `layoutEffect` can you execute me next, please?"

And this discussion happens to each fiber node until we get back to the **Root fiber node**.

Then the **Root fiber node** has just to execute its `firstEffect`.

This effect in our case, will be the one that has the **ref flag** that has already used previously. Because React detects the flag it will then attach the **DOM node** into the ref if it's an object of pass it as a parameter if it's a function (see callback ref in the previous part). 

I want to make an article dedicated to how works React under the hood, hoping you will enjoyed it. If it's case do not hesitate to tell me in comments to give me motivation <3


## Conclusion

**React ref** has multiple use cases that we have seen previously, do not hesitate to tell when you are using them.
The things you need to keep in mind:
- changing a `ref` will no trigger a re-render
- do not update / read a `ref` directly in `render` but in `useEffect` / `useLayoutEffect` and event handlers. Except when doing lazily initialization.
- do not overused **React state** when in fact you do not need to use the value for the UI.
- when you use a `ref` to prevent putting a dependency on `useEffect` / `useLayoutEffect` or `useCallback` that should not trigger the execution of the effect / re-creation of the callback. Do not forget to update in a `useEffect` / `useLayoutEffect`.
In a next article, we will see that `ref`s are also useful to use the native hook named `useImperativeHandle`.

----

Do not hesitate to comment and if you want to see more, you can follow me on [Twitter](https://twitter.com/romain_trotard ) or go to my [Website](https://www.romaintrotard.com).