Track Values Over the Course of Renders with React useRef in a Custom usePrevious Hook

Kent C. Dodds
InstructorKent C. Dodds

Share this video with your friends

Send Tweet

Our hook to track the previous values looks pretty useful, so let's extract that into it's own custom React Hook called usePrevious.

Instructor: [00:00] Let's go ahead and take this a step further and extract some more Hooks that could be potentially useful elsewhere in our code base. Like this, for instance. It could be useful to be able to keep track of previous value over the course of renders.

[00:12] I'm going to take this. I'll pull this out entirely, and I'm going to make a new Hook called usePrevious. That's going to take a previous value, and I'm going to paste that in there.

[00:24] We're going to get a ref. We'll just make this more generic. We'll just call it ref. The current is going to be the value. What will we return? Let's go ahead and return the ref.current. We'll just return the current value. We don't need to return the entire ref.

[00:38] Let's go back in here. We'll say usePrevious, and we're going to get our previous inputs. Our inputs that we want to keep track of is this variable and query array here. Now we're getting the same thing that we had before, except we no longer need to use .current, because this is the actual value that we care about.

[00:59] In review, to make usePrevious, we simply made a function that accepts a value. We created a ref to keep a reference of that previous value. Then, in an effect, we have a callback that runs after every single time our component renders where we update the current value to that value, and then we return ref.current.

Brian Eichenberger
Brian Eichenberger
~ 2 years ago

this took me a minute to understand.

function usePrevious(value) {
  const ref = useRef()
  useEffect(() => {
    ref.current = value
  })
  return ref.current
}

until I remembered that useEffect() will be only be called AFTER render. so the value that you pass into usePrevious() is setup to be used next time, and the ref.current which is returned is the one which was assigned last time.

function usePrevious(value) {
  const ref = useRef()
  useEffect(() => {
    ref.current = value  // this ref.current will be returned next time usePrevious is called.
  })
  return ref.current 
 //  this ref.current was assigned last time usePrevious() was called.  if this is the first time usePrevious() has been called, ref.current will be undefined (unless you assign it something in the useRef hook.)
}

... which is exactly the point of that function. it just took me a minute to figure out why it worked, what was going on. :)

Halian Vilela
Halian Vilela
~ 2 years ago

Nice comment, @Brian... thanks for the tip!

Mickey
Mickey
~ 2 years ago

@Brian thanks for that, I too had to stop and look at this, your comment helped!