Using React ref and really understanding it are two different pair of shoes. To be honest, I am not sure if I understand everything correctly to this date, because itâs not as often used as state or side-effects in React and because its API did change quite often in Reactâs past. In this React Ref tutorial, I want to give you a step by step introduction to refs in React.
React refs are strongly associated with the DOM. This has been true in the past, but not anymore since React introduced React Hooks . Ref means just reference, so it can be a reference to anything (DOM node, JavaScript value, â¦). So we will take one step back and explore the React ref without the DOM first, before diving into its usages with HTML elements. Letâs take the following React component as example:
React offers us the React useRef Hook which is the status quo API when using refs in React function components . The useRef Hook returns us a mutable object which stays intact over the lifetime of a React component. Specifically, the returned object has a current property which can hold any modifiable value for us:
The refâs current property gets initialized with the argument we provide for the useRef hook (here false ). Whenever we want, we can reassign the refâs current property to a new value. In the previous example, we are just tracking whether the button has been clicked.
The thing about setting the React ref to a new value is that it doesnât trigger a re-render for the component. While the state updater function (here setCount ) in the last example updates the state of the component and makes the component re-render, just toggling the boolean for the refâs current property wouldnât trigger a re-render at all:
Okay, we can use Reactâs useRef Hook to create a mutable object which will be there for the entire time of the componentâs existence. But it doesnât trigger a re-render whenever we change it â because thatâs what state is for â, so whatâs the refâs usage here?
The ref can be used as instance variable for a function component in React whenever we need to track some kind of state without using Reactâs re-render mechanism. For example, we can track whether a component has been rendered for the first time or whether it has been re-rendered:
In this example, we initialize the refâs current property with true, because we assume rightfully that the component starts with its first render when it gets initialized for the first time. However, then we make use of Reactâs useEffect Hook â which runs without a dependency array as second argument for the first and every additional render â to update the refâs current property after the first render of the component. Setting the refâs current property to false doesnât trigger a re-render though.
Now we gain the ability to create a useEffect Hook which only runs its logic for every component update, but not for the initial render. Itâs certainly a feature which every React developer needs at some point but which isnât provided by Reactâs useEffect Hook:
Deploying instance variables with refs for React components isnât widely used and not often needed. However, I have seen developers from my React workshops who surely knew that they needed an instance variable with useRef for their particular case after they have learned about this feature during my classes.
Rule of thumb: Whenever you need to track state in your React component which shouldnât trigger a re-render of your component, you can use Reactâs useRef Hooks to create an instance variable for it.
Letâs get to Reactâs ref speciality: the DOM. Most often you will use Reactâs ref whenever you have to interact with your HTML elements. React by nature is declarative, but sometimes you need to read values from your HTML elements, interact with the API of your HTML elements, or even have to write values to your HTML elements. For these rare cases, you have to use Reactâs refs for interacting with the DOM with an imperative and not declarative approach.
This React component shows the most popular example for the interplay of a React ref and DOM API usage:
Like before, we are using Reactâs useRef Hook to create a ref object (1). In this case, we donât assign any initial value to it, because that will be done in the next step (2) where we provide the ref object to the HTML element as ref HTML attribute. React automatically assigns the DOM node of this HTML element to the ref object for us. Finally (3) we can use the DOM node, which is now assigned to the refâs current property, to interact with its API.
The previous example has shown us how to interact with the DOM API in React. Next, you will learn how to read values from a DOM node with ref. The following example reads the size from our element to show it in our browser as title:
As before, we are initializing the ref object with Reactâs useRef Hook, use it in Reactâs JSX for assigning the refâs current property to the DOM node, and finally read the elementâs width for the componentâs first render via Reactâs useEffect Hook. You should be able to see the width of your element as title in your browserâs tab.
Reading the DOM nodeâs size happens only for the initial render though. If you would want to read it for every change of the state, because thatâs after all what will change the size of our HTML element, you can provide the state as dependency variable to Reactâs useEffect Hook. Whenever the state (here text ) changes, the new size of the element will be read from the HTML element and written into the documentâs title property:
Both examples have used Reactâs useEffect Hook to do something with the ref object though. We can avoid this by using callback refs.
A better approach to the previous examples is using a so-called callback ref instead. With a callback ref, you donât have to use useEffect and useRef hooks anymore, because the callback ref gives you access to the DOM node on every render:
A callback ref is nothing else than a function which can be used for the HTML elementâs ref attribute in JSX. This function has access to the DOM node and is triggered whenever it is used on a HTML elementâs ref attribute. Essentially itâs doing the same as our side-effect from before, but this time the callback ref itself notifies us that it has been attached to the HTML element.
Before when you used the useRef + useEffect combination, you were able to run the your side-effect with the help of the useEffectâs hook dependency array for certain times. You can achieve the same with the callback ref by enhancing it with Reactâs useCallback Hook to make it only run for the first render of the component:
You could also be more specific here with the dependency array of the useCallback hook. For example, execute the callback function of the callback ref only if state (here text ) has changed and, of course, for the first render of the component:
However, then we would end up again with the same behavior like we had before without using Reactâs useCallback Hook and just having the plain callback ref in place â which gets called for every render.
So far, we have used the DOM ref only for read operations (e.g. reading the size of a DOM node). Itâs also possible to modify the referenced DOM nodes ( write operations ). The next example shows us how to apply style with Reactâs ref without managing any extra React state for it:
This can be done for any attribute on this referenced DOM node. Itâs important to note that usually React shouldnât be used this way, because of its declarative nature. Instead you would use Reactâs useState Hook to set a boolean whether you want to color the text red or blue. However, sometimes it can be quite helpful for performance reasons to manipulate the DOM directly while preventing a re-render.
Just for the sake of learning about it, we could also manage state this way in a React component:
Itâs not recommended to go down this rabbit hole though⦠Essentially it should only show you how itâs possible to manipulate any elements in React with Reactâs ref attribute with write operations. However, why do we have React then and donât use vanilla JavaScript anymore ? Therefore, Reactâs ref is mostly used for read operations.
This introduction should have shown you how to use Reactâs ref for references to DOM nodes and instance variables by using Reactâs useRef Hooks or callback refs. For the sake of completeness, I want to mention Reactâs createRef() top-level API too, which is the equivalent of useRef() for React class components . There are also other refs called string refs which are deprecated in React.