DEV Community

Cover image for Storing values with the useRef hook
Emma Goto πŸ™
Emma Goto πŸ™

Posted on • Originally published at emgoto.com on

Storing values with the useRef hook

React's useRef hook is used to store references to DOM elements. But did you know you can store and update values with useRef?

Storing element references with useRef

As the name suggests, useRef can store a reference to a DOM element. To do this, create the ref, and then pass it into the element:

const Component = () => {
    const ref = useRef(null);
    return <div ref={ref}> Hello world </div>;
};
Enter fullscreen mode Exit fullscreen mode

With this reference, you can do lots of useful things like:

  • Grabbing an element's height and width
  • Seeing whether a scrollbar is present
  • Calling focus() on the element at a certain moment

Storing and updating values with useRef

Another use-case for useRef allows us to store values, which you can later use and change:

const Component = () => {
    const ref = useRef({
        renderCount: 0
    });

    // Increase the render count on every re-render
    ref.current.renderCount += 1;

    return <>Hello world</>;
}
Enter fullscreen mode Exit fullscreen mode

To change the ref's value, you will need to change ref.current (and not ref itself!)

useRef vs useState: What's wrong with useState?

The key difference between useState and useRef is that:

  • If you update the state, your component will re-render
  • If you update the value stored in your ref, nothing will happen

If you don’t need the component to re-render (or you don't want the component to re-render), useRef may be a good candidate.

What’s the difference between useRef and using a variable?

If you tried initializing a variable like this:

const Component = () => {
    let renderCount = 0;
    renderCount += 1;

    return <>Hello world</>;
}
Enter fullscreen mode Exit fullscreen mode

It would get end up getting re-initialized each time the component renders. If you use a ref, the value you store in it will persist between renders of your component.

What about if I define the variable outside of the component?

If you initialize the value outside of your component, this value will be global to all instances of Component.

So if you change the value, it will affect the value for all the other components you have rendered on your page.

let renderCount = 0;

const Component = () => {
    // If you had 10 Components on the page, they would all update the same
    // renderCount value and it would already be at 10 after one render!
    renderCount += 1;
    return <>Hello world</>;
}
Enter fullscreen mode Exit fullscreen mode

useRef vs createRef

createRef is the API that React provided for refs before hooks came around:

import { createRef } from 'react';

class Component extends React.Component() {
    ref = createRef();

    render() {
        this.ref.renderCount += 1;
        return <div>Hello world</div>;
    }
}
Enter fullscreen mode Exit fullscreen mode

If you're using functional components I would recommend using useRef over createRef.

How to use useRef in class components

As useRef is a hook, it will only work with functional components.

With class components, you can use the createRef() example I showed above.

You can also achieve the same thing using a class variable:

class Component extends React.Component() {
    renderCount = 0;

    render() {
        this.renderCount += 1;
        return <div>Hello world</div>;
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (5)

Collapse
 
caiangums profile image
IlΓͺ Caian

Hey there! Nice written article!

Personally I can't see a real use-case of this except for counting re-renders and if you could provide one or two it'll be nice πŸ˜„

As ReactJS is leading to a more Functional approach with Immutability it is nice doesn't have side effects and just use variables inside Component State (using the useState() hook), and for Application State is common the usage of libs like Redux.

One good reference for this is this article as Updating a ref value is a side effect and side effects should be avoided or even dealt inside the useEffect() hook.

The main point I'm saying here is: you can use useRef() for storing values but you shouldn't.

Again, thanks for sharing some knowledge with us! πŸ€—

Collapse
 
emma profile image
Emma Goto πŸ™

Hi! Good point on keeping their usages inside of useEffect.

Storing variables in useState will be fine most of the time, but if you're in a situation where you're concerned about unnecessary re-renders impacting performance, this can be a useful way of getting around that. A real use-case might be if you had some sort of isFetching or hasMounted boolean that you need to keep track of.

Collapse
 
nickksm profile image
nickksm • Edited

I was just building an image slider/swipe like Instagram, using transform translateX css to slide the image. I needed to store the "evt.touches[0].clientX" value (current touch position), and later use that value in the transform translateX.

Every time the finger slides, I needed to re-update the value so I'll know which way the user is sliding. Storing this value in a state causes a LOT of unnecessary re-renders. Using useRef to store values without re-render was exactly what I needed.

Collapse
 
kildareflare profile image
Rich Field

Dan Abramaov has a great post on using this feature of hooks to make setInterval declarative.
The hook is used to store a reference to a callback.
overreacted.io/making-setinterval-...

Collapse
 
emma profile image
Emma Goto πŸ™

Interesting, I'll give that a read. Thanks!