A Look At React Hooks: useRef

A Look At React Hooks: useRef

Introduction to the useRef Hook + bonus

Welcome to A Look at React Hooks, a beginner-friendly series on React Hooks. In this article, let's look at the useRef Hook.

What is useRef?

useRef returns a mutable object with a single property: current, that stores the value of the reference element like so:

{ current: ReactElementReference }

This stored value persists for the entire lifetime of the component and will not re-render the component when the value of current is mutated.

Hence, this Hook is useful for storing and updating values that are frequently changing because as mentioned in this article, the useState Hook will trigger a re-render on every state update.

Another reason to use this Hook is when you want to imperatively access a DOM element, and altering its state according to what you want it to do.

To do this, simply add a ref attribute in an element created in its render method. For example, <div ref={myRef}>. Now, React is able to access this <div> element directly. We will see how this Hook allows React to access the element soon.

In short, use useRef when you want to:

  1. Store and update mutable information without triggering re-renders all the time
  2. Access variables that are persisted across re-renders
  3. Imperatively access DOM nodes via refs to perform some functions

Initialization

First, import the Hook:

import React, { useRef } from 'react';

Then, declare and initialize the ref:

// initializes the current property of the ref aka 
// inputElement.current = null
const inputElement = useRef(null);
// useRef now returns {current: null}

Simple Example

Now, we can use this Hook to access the input element imperatively by having the ref attribute of <input> equal to inputElement:

  return (
    <div>
       <!--passing in inputElement as ref-->
      <input ref={inputElement} type="text" />
      <button onClick={onButtonClick}>SUBMIT</button>
    </div>
  );

Accessing an element this way is how we can access methods that we normally can do with document.getElementById() in vanilla JavaScript. Like focus() or .value. Let's write the onButtonClick function in our above example.

const onButtonClick = () => {
    inputElement.current.focus(); // focuses the ref element
    console.log(inputElement.current.value) // prints the value of the ref
  };

And that's the basics of useRef! Pretty straightforward, isn't it? So let's take it further by learning a closely related Hook: useImperativeHandle.

useImperativeHandle

A Hook that is often used with useRef is useImperativeHandle. Basically, it hides the DOM node reference from its parent component and exposes only the properties you want.

The Hook accepts a ref object and a handler function. This handler function specifies the properties of the ref that its parent component can access. So the parent of the ref will not be able to access the entire ref object; just the ones in the handler function. The Hook must be used with forwardRef so that the ref attribute can be passed to another component. Let's look at an example.

Read more about forwardRefs here

Example

Here's a simple example of the useImperativeHandle Hook in action. Code is taken from React Docs. Let's understand it step-by-step.

//FancyInput component
function FancyInput(props, ref) {
  //initialize useRef
  const inputRef = useRef();

  //initialize useImperativeHandle
  //it accepts ref as 1st arg and the handler function
  //returns the inputRef.current.focus() method
  //any parent component will only be able to access
  //focus property, not the inputRef itself
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));

  //sets the input element as the ref
  return <input ref={inputRef} ... />;
}

//use forwardRef to pass the ref attribute
FancyInput = forwardRef(FancyInput);

In the code above, we have a FancyInput component and its <input> element as its ref object. The useImperativeHandle Hook then takes this ref object as its 1st argument and returns an object in its handler function as its 2nd argument. This object includes focus() method of the current property of the ref.

Let's say <App/> is the parent of FancyInput. It will only be able to access inputRef.current.focus() instead of the input element itself as seen in the image below. Whereas FancyInput itself gets access to the entire inputRef.

image.png

Conclusion

And that's the gist of this Hook! Thanks for reading this article. I hope it was helpful for React beginners. Please feel free to ask questions in the comments below. Ultimately, practising and building projects with this Hook will help anyone to pick it up faster.

The next Hook in this series will be: useReducer. Stay tuned and cheers!


Resources

Did you find this article valuable?

Support Victoria Lo by becoming a sponsor. Any amount is appreciated!

ย