useRef and useState, which one should you use and why?
How well do you know useRef and useState? Well, if you have any doubts about it, you and I need to have a little talk. First things first, I feel the need to reiterate how important it is to understand useRef and useState hooks. Apart from all the optimization talk, useRef and useState are two of the core important hooks in React. Let’s talk about useState first.
According to the official docs, they return a stateful value and a function to update it but that doesn’t mean much, does it? Well, useState allows you to declare a state and a function to manipulate the state you’ve declared. This article assumes you at least have a basic understanding of React so I won’t dive deep into what state is and why you need to manipulate it. However, there’s an important piece of information you need to know. Every time you manipulate the state with useState, the component gets re-rendered but it doesn’t end there either. Regardless of whether they consume that component as a prop or not, the children of that component are also re-rendered. Let’s look at a very basic counter-app example.
We have a state called “count” and its initial value is 0. We have a setter function called “setCount.” We’re using the useEffect hook to console log “count rendered” every time the count state changes. We’re displaying the current value of the count state within p tags and we have 2 buttons side by side to increase and decrease the value of our count state. Now, if you check your console, you’ll see that you already have a log even if you didn’t click any button. That’s because the state changes before the virtual DOM is painted. In simpler words, state changes before we can even see the website. Now, if you click any button, be it increase or decrease, you’ll see continuous console logs telling you that the count was rendered. That’s because useState re-renders our component. Not only that, but if you’re using React DevTools(which you should) you’ll notice that our buttons are also being re-rendered. Well, there are ways to prevent the entire component from being re-rendered but you can see why useState needs to be used with more care.
useRef allows you to store a mutable reference object. Similar to useState, you can store all types of different values. I altered our code slightly for useRef.
I declared a reference object and called it “count.” Its current value is 0. Most of it is still the same code so I won’t go into too much detail. However, it’s worth noting that “count” is now an object. It has a property called “current” and you use the current property to manipulate the data. Now, you can keep pressing increment or decrement buttons like a mad man but nothing will change on the page itself. However, if you press “console log current value” and check the console, you’ll notice that the value is changing but as our component doesn’t get re-rendered, you don’t notice the change as it occurs.
Okay, that’s cool but why did I explain all this to you? Well, there are times your end-user doesn’t need to see the state as it’s updated. Assume that you’re making a login page and you want to store the current login attempts on your frontend for some reason. If you used useState to accomplish it, your login form would get re-rendered every time the end-user tried to log in and that’s less than ideal. If you used useRef, you could still store the number of login attempts and the component wouldn’t be re-rendered. If you’re using classes and instance variables, you need to work with useRef. Refs are also useful for user input handling. Feel free to play around with useRef and useState and let me know your use cases for both in the comments section. With that being said, that’s all I have for you guys today.
Thanks for reading and do let me know if you have any questions. See you next time!