In the course of working on a project for Flatiron School, I stumbled upon a use case for a React hook that I had yet to encounter. It was actually in compiling my code that my console suggested the useRef hook and I'd like to show how it solved my problem.
A < div /> in Motion
The central idea behind my project involved getting a div element to "bounce" off a boundary and come back. I initially used a combination of useState and useEffect to achieve this within the component I intended to bounce.
Because I needed to actually re-render the component to reflect where its calculation had moved it to, I needed to nest a setInterval() inside a useEffect hook. The reason I am returning a cleanup function here to clear my clock interval is because without it, a new setInterval would be created on every state change until they all piled up and updated the component's position wayyy too many times.
The bouncer at the start of its journey.
Everything seemed fine with this approach until I got to a point where multiple state changes need to happen synchronously. Here's what happens when this code reaches the boundary condition at 93 viewport width units:
As you can see, it's just running into the wall over and over (much like myself trying to fix this problem).
After console.logging extensively and reviewing state, I localized the problem to my state variable for the current direction of my component (xSpeed) not being able to change fast enough to accurately change the value of my xPosition state. So when the variables become unsynced due to not having updated together as they should've, xSpeed is constantly flipped due to xPosition being on the right side of the boundary, a place I hadn't considered possible to reach.
Another thing about the way I implemented my bouncing div is that state was constantly updating, which is a lot of re-rendering. In attempting to fix this without useRef, I managed to cause my first infinite re-render loop. I could've reworked the way I assigned the value for state or just made one state calculable from the other by perhaps refactoring my whole code for this, but the error I got from running this on a development server actually suggested the useRef hook as a fix.
When is a State Not a State?
So what does useRef do to remedy this? Well, to put it in an overly simplistic way, it offers a way to make a state variable that can be updated without triggering a re-render. Here's the changed section of my code with the useRef hook instead of another state.
Note how I am still re-rendering through useEffect with the position of my bouncing div.
useRef creates an object with a special property under the object's .current key. This key has a value that you can set to whatever you like, just like useState, but this property is readable just like a variable you're used to in JavaScript. In my implementation of it, it means that when I update this div's speed to -1, I am immediately able to access xSpeed.current's new value and subtract 1 from my xPosition state. This fixes my logic and reduces the number of re-renders needed.
And now we get a proper bounce!
TL;DR
So, if you ever need to have a variable stored in a component like a state, but you don't want to re-render every time you re-assign the value of that variable, consider useRef. If you ever need to reassign state and aren't patient enough to wait for a re-render before accessing that new state for another statement, definitely consider useRef.
Top comments (0)