A horse looking at you with its sexy bangs

Create a stable random value with React.useRef

  • Kristofer Giltvedt Selbekk

Have you ever tried to create a random value in React? It's not as straight forward as you might think. Let me show you with an example.

const images = ['/cat.jpg', '/dog.jpg', '/bear.jpg'];
const RandomImage = () => {
  const image = images[Math.floor(Math.random() * images.length)];
  return <img src={image} alt="" />;
}

This might look correct at first glance, but it has a very unfortunate side effect - it will choose a new random picture on every render. Here's what I'm talking about:

We need to find a way to create a stable random value - a random value that stays the same across renders. Now, how can we achieve that?

Refs to the refscue!

React's built-in useRef hook is one of my favorite tools for a lot of different applications. You can use them to store references to DOM elements or other components, of course, but they also have another really clever use case:

Whatever you place into React.useRef will stay there indefintely. That means, we can basically just put the random image picking code from above into a useRef, and it will stay the same across re-renders!

Let's change our code:

const images = ['/cat.jpg', '/dog.jpg', '/bear.jpg'];
const RandomImage = () => {
  const image = React.useRef(images[Math.floor(Math.random() * images.length)]);
  return <img src={image.current} alt="" />;
}

Remember that the value now lives in image.current instead of in image directly.

And here's a much more stable example:

Over-engineer it

If you want, and you have the need for it, this makes for a great custom hook. We can create one that picks out a random item from an array for example:

const useRandomItem = (array) => {
  return React.useRef(array[Math.floor(Math.random() * array.length)]).current;
}

Note that we now return the actual value instead of the ref object with its current field. That makes it much more intuitive to use!

And POW - now, we can just refer to this bad boy whenever we want a random item from an array:

const images = ['/cat.jpg', '/dog.jpg', '/bear.jpg'];
const RandomImage = () => {
  const image = useRandomItem(images);
  return <img src={image} alt="" />;
}

A note on useMemo

As with many things in life, there are several ways to attain the same effect. Another approach is to use another built-in hook - namely useMemo.

const useRandomItem = (array) => {
  const randomValue = React.useMemo(() => Math.random(), []);
  return array[Math.floor(randomValue * array.length)];
}

I personally prefer using React.useRef because that's what I came up with first and I hate relearning stuff, but there's nothing in the way of using useMemo instead.

Thanks for reading! I hoped it helps you make amazing stuff.