How to use useRef in React

December 09, 2020

A post by Will Mayger
Twitter . Instagram

Learn, step by step, how to use useRef in React

When you use React you are most likely going to have times where you need to use the useRef hook.

It will be useful to understand how to use useRef in react, and its many uses so you can identify when you can use it and when not to use it.

To name a few of the uses for useRef in react that we are going to cover would be, DOM refs, persisting values, mutability, forwarding, modules, and more.

As you can see there are quite a lot of things you can do with useRef, and there are probably many more uses.

So, let’s take a look at some of the basic principles first to help us understand it, and then afterwards we can look at some of the other uses for it.

Before we dig in, just a quick note, everything here is intended for all experience levels and will be explained without too much technical jargon!

The basics of how to use useRef in React

You can think of useRef as useReference, just like if you are referencing a variable.

One of the primary uses of the hook useRef that you might already be aware of is to be able to associate a DOM element with it, but it is by no means it’s only use though which we will talk about more later on.

When you use a ref to access a DOM element you are essentially assigning that DOM element to a state that won’t cause a re-render whenever it gets updated.

React assigns the ref to the useRef behind the scenes, this is where the current value comes into play.

useRef will allow you to assign any value to its current property.

Let’s look at a quick example of assigning a ref to a DOM element in react:

import React, { useRef } from "react"
export function MyComponent() {
  const divRef = useRef(null)

  return <div ref={divRef} className="test" />
}

When you add in the ref using <div ref={divRef} /> what is really happening is React is assigning the reference of that DOM element to the useRef’s current property everytime that element changes.

This means that the useRef’s current property is mutable.

So, what does mutable mean and what has it got to do with useRef?

Mutability, is at useRef’s core, unlike how most of react works, useRef uses a mutable property called current.

All it means when something is mutable is that it can be changed or updated.

Unlike useState which returns a setter function that will re-render the whole component when updating the state, with useRef nothing will be re-rendered when you update it.

Okay, back to our DOM element, as you can see we define the ref before it gets associated with our DOM element.

This is something you do need to be careful about and can be confusing to start with. You need to be prepared for the useRef value to be null when you first use it so make sure you write a condition or use optional chaining to handle this to prevent bugs and errors.

Here is an example of this:

import React, { useRef, useEffect } from "react"

export function MyComponent() {
  const divRef = useRef(null)

  useEffect(() => {
    // optional chaining example
    console.log(divRef.current?.className)

    // conditional example
    if (divRef.current !== null) {
      console.log(divRef.current.className)
    }
  })

  return <div ref={divRef} className="test" />
}

So why do we need to reference an element?

In React it is somewhat uncommon to need to use a ref, and you should generally avoid it if you can.

With that said sometimes you do need to use it and there is nothing wrong with using it as long as it isn’t a workaround or quick fix for something that could be done in another way.

A time you might use it would be to get the dimensions of an element.

Let’s take a look at the example below to see how to use useRef in React to get the element dimensions:

import React, { useRef, useEffect, useState } from "react"

export function MyComponent() {
  const [dimensions, setDimensions] = useState(null)
  const divRef = useRef(null)

  useEffect(() => {
    if (!divRef?.current) {
      return
    }

    // Using divRef?.current?.getBoundingClientRef() will get the dimensions
    // Adding  || { height: null, width: null } is an example of a fallback
    const { height, width } = divRef?.current?.getBoundingClientRect() || {
      height: null,
      width: null,
    }

    if (height !== null || width !== null) {
      setDimensions({
        height,
        width,
      })
    }
  })

  return (
    <div ref={divRef} className="test">
      <p>
        {dimensions?.height}
        {dimensions?.width}
      </p>
    </div>
  )
}

As you can see we are creating our ref using the useRef react hook, and in the JSX we are assigning it to an element.

We have a function that runs on every render with the help of useEffect that is going to log the elements dimensions.

That will sum up the basics of using useRef with DOM elements.

How to store values with useRef in react

Storing values with useRef is probably one of the most under-rated things in React that you don’t often find so remain unaware of.

When you do come across this for the first time you might even think it looks wrong or is an anti-pattern (It was strange to me the first time I saw it!).

However, if you take a quick read through the official React docs for useRef you will quickly see it is actually an intended use for it, not just as a ref for elements.

This comes in handy whenever you need to store a variable that you do not want to cause a re-render.

Imagine it a bit like the useState hook but without the immutable (can’t be updated) value and setter function, and instead you are given a mutable (can be updated) value.

In other words, to answer useRef vs useState, useRef is mutable with no setter function and causes no re-renders.

So where might you use this and how can you use it?

Let’s take a look at the below example so we can see how we can make use of a useRef and where it might be useful.

import React, { useRef } from "react"

export function MyComponent() {
  const renderRef = useRef({ count: 0 })

  if (typeof renderRef.current?.count !== "undefined") {
    renderRef.current.count += 1
  }

  return (
    <div>
      <p>Render count: {renderRef.current?.count || 0}</p>
    </div>
  )
}

How to forward refs with useRef

So, what is ref forwarding?

Forwarding a ref is simply passing the ref of a parent component down to child components. The parent has the ref which we created with useRef, and then the child can access and use that ref after we forward it.

This is pretty straightforward and React provides us with a handy HOC(Higher order component - a function that accepts a component and returns a component) to be able to make use of this.

In your parent component you just add the ref into the child as if the child component was a DOM element.

In the child component we need to wrap it in the HOC React.forwardRef(), and there we have a forwarded ref!

Here is an example of how this looks:

import React, { useRef } from "react"

export const MyChildComponent = React.forwardRef((props, ref) => {
  return <div ref={ref} className="test" />
})

export function MyParentComponent() {
  const divRef = useRef(null)

  return (
    <div>
      <MyChildComponent ref={divRef} />
    </div>
  )
}

You can read the official docs on ref forwarding here.

Real life uses for useRef in react

Now before we finish off this article, let’s take a look at some real-life uses for useRef that are really useful.

Google maps with useRef

When you use Google maps with React, you will most likely be using a library/module that you installed from npm.

Most of these modules will allow you to directly access the google map as a ref.

Here is an example of using useRef with Google maps in REact:

function Marker = ({ map, name, image, geometry }) => {
  if (!map) {
    return null
  }

  const marker = new window.google.maps.Marker({
    icon: image,
    title: place.name,
    map, // map ref is passed in here
    position: geometry.location,
  })

  return null
}

export default function MyMap({ marker }) {
  const mapRef = useRef(null)

  return (
    <GoogleMapReact
      onGoogleApiLoaded={({ map }) => {
        mapRef.current = map
      }}
    >
      <Marker marker={marker} map={mapRef.current} />
    </GoogleMapReact>
  )
}

Dynamically updating element attributes with useRef

At some point you may end up having a need to update elements after they have loaded in for some reason or another, I personally have had to do this in the past to add pinterest id’s to images on a given page.

Here is an example of using useRef to dynamically add in an attribute to an element:

import React, { useRef, useEffect } from "react"

export function MyComponent() {
  const divRef = useRef(null)

  useEffect(() => {
    divRef.current?.setAttribute("data-id", "Qvdfgshh")
  })

  return <div ref={divRef} className="test" />
}

Scrolling with useRef

You might need to create a parallax, or log information for analytics, but there are many cases for when you need to track or modify scroll in certain places.

This is something that you might look into using the useRef hook for.

Here is an example of a hook to handle scrolling in react with useRef:

import { useEffect, useRef } from "react"

export default function useScrollTo(scrollToPosition) {
  const domRef = useRef(null)

  useEffect(() => {
    if (!domRef.current) {
      return
    }

    domRef.current.scrollTo(0, scrollToPosition)
  }, [scrollToPosition])

  return domRef
}

Creating a hook to emulate componentDidMount with useRef

The last real world example that I will provide is when you want to emulate componentDidMount with a react hook, but you could not do it with the more conventional solutions.

I won’t get into it in too much detail here because I have a whole article dedicated to using componentDidMount with react hooks here.

Simply put, there are better ways to handle this in most cases, but there might be a rare instance where you do need to take matters into your own hands.

Here is an example of using useRef to emulate componentDidMount:

import { useEffect, useRef } from "react"

export default function useDidMount(callback) {
  const didMount = useRef(null)

  useEffect(() => {
    if (callback && !didMount.current) {
      didMount.current = true
      callback()
    }
  })
}

Summary

That about covers everything you will need to know in order to get started with how to use useRef in React!

I hope you have enjoyed this article!

Will

Image icon made by Freepik from www.flaticon.com