Use useRef hook to store values you want to keep an eye on

ยท 2 mins read ยท ย React

The most common use of refs in React is to create references to DOM elements and run imperative logic when needed. Force elements to become focused, scroll up and stuff like that. React hooks take all this a step further so we can use refs to store values we need to watch. The hook we use to achieve all these is useRef.

# Create references for DOM elements

Let's see one common example by creating a reference to a DOM element.

import React from 'react';

const TextInput = () => {
  const inputRef = React.useRef(null);

  React.useEffect(() => {
    inputRef.current.focus();
  }, []);

  return (
    <div>
      <label for="name">Name<label>
      <input id="name" ref={inputRef} />
    </div>
  );
};

So we created a function component named TextInput. This very component returns an input element. We created a reference to this DOM element by using useRef. With the help of useEffect hook we run some imperative logic every time this components renders for the first time so it becomes focused no matter what. Awesome!!

# Create references for values

So above we saw the most common use of useRef hook but there is an even more interesting one. We can use it and store in memory values we need to keep an eye on.

Before hooks era we were using mostly lifecycles hooks to check for props or state attributes changes and run some logic right after. Lifecycle hooks like componentDidUpdate, componentWillReceiveProps etc were used to accomplish this.

So how do we do this? Let's see an example. Imagine we have a simple component that takes the exchange rate of USD to EUR. We need to return the exchange rate and provide some indication about the tension. So when the rate increases we need to make it green. When the rate gets decreased we have to make it red. Last but not least, when it stays still we need to make it gray.

Let's create a super simple presentational component first:

import React from 'react';

const ExchangeRate = ({ rate }) => <div>{rate}</div>;

Super simple stuff. Let's start working on the coloring logic. Since we need to watch for rate changes we need to take advantage of useEffect.

import React from 'react';

const ExchangeRate = ({ rate }) => {
  React.useEffect(() => {
    // run some logic when rate changes
  }, [rate]);

  return <div>{rate}</div>;
};

Ok, useEffect is in place. We need to introduce local state management mechanism in order to update color selection and force a re-render every time state gets updated.

import React from 'react';

const ExchangeRate = ({ rate }) => {
  const [color, setColor] = React.useState('gray');

  React.useEffect(() => {
    // run some logic when rate changes
  }, [rate]);

  return <div style={{ color }}>{rate}</div>;
};

We added useState hook with initial value the gray color in order to have a sane default value. Now we can simply use color attribute from state to paint our rate.

It is about time to setup our logic in useEffect every time rate prop gets updated. We miss something though. When rate changes and useEffect gets triggered we have access to the brand new value of rate. We don't have access to it previous one so we cannot make a comparison.

In order to accomplish this, we need useRef hook. We will use it to store rate value in useEffect so the very next time that rate gets updated we will still have access to its previous value. We will use that reference to make the right color selection and then we will update it accordingly in order to prepare the ground for the next cycle.

Reference is a mutable object and the value is stored under current key:

// reference object
{
  current: 'value is stored here'
}

Updating the reference doesn't force a re-render so we can safely store there values we need to watch without any side-effects. The only thing we need to take care of is to update them manually since they don't update automatically.

Time to refactor our component:

import React from 'react';

const ExchangeRate = ({ rate }) => {
  const rateRef = React.useRef(null);
  const [color, setColor] = React.useState('gray');

  React.useEffect(() => {
    const oldRate = rateRef.current;

    if (rate > oldRate) {
      setColor('green');
    } else if (rate < oldRate) {
      setColor('red');
    } else {
      setColor('gray');
    }

    // Don't forget to update reference manually
    rateRef.current = rate;
  }, [rate]);

  return <div style={{ color }}>{rate}</div>;
};

Some of you might think that we could use useReducer but hey, let's keep things simple whenever we can ๐Ÿ˜‰

Newsletter

Get notified about latest posts and updates once a week!!