DEV Community

Cover image for useRef, forwardRefs and useImperativeHandle.
Zidane Gimiga
Zidane Gimiga

Posted on • Edited on

useRef, forwardRefs and useImperativeHandle.

Picture this. You are creating a form on your React/React Native application, and you want an input tag to be focused as soon as the form is rendered and mounted on the screen. I'm writing to share my learnings on how to access a DOM node's property in React, control which properties of the node you would like to be exposed and be able to pass refs from a parent to a child component.

Let's start with the basics. What is a ref? To put it simply, it's a label or name for a specific thing or location where something is stored. In React, you may want to access, interact and directly manipulate a DOM node like <input/>, <p> and so on.

The React library has a hook that allows you to define refs and attach them to specific DOM elements you may like. It's called useRef.

import { useRef } from "react";

const Form = () =>{
  const inputRef = useRef(null);

  return(
    <form>
       <input ref={inputRef}/>
    </form>
  )
}

Enter fullscreen mode Exit fullscreen mode

In the above code we are importing useRef, defining a component Form, adding an input tag wrapped by a form tag. We create a reference to the input tag called inputRef and initialize it as null. This allows us to do something cool like, focusing the input as soon as the form renders. For this, we'll utilize the useEffect hook as follows:

import { useRef, useEffect } from "react";

const Form = () =>{
  const inputRef = useRef(null);

  useEffect(()=>{
    inputRef.current.focus();    
  }, [])

  return(
    <form>
       <input ref={inputRef}/>
    </form>
  )
}

Enter fullscreen mode Exit fullscreen mode

With the ref, you can perform more things like setting style properties e.g

  inputRef.current.style.borderColor = "green";
Enter fullscreen mode Exit fullscreen mode

It basically exposes all of the properties of a DOM node to you.

Now, let's say you are creating a MyInput component because of your design system and it's also a best practice to create reusable components in React so as to avoid code duplication.

Being this independent, you can pass a ref to a specific input component. For this, we utilize forwardRef.

Your input component might look like:

import { forwardRef } from "react";

const MyInput = forwardRef(function MyInput(ref, props)){
  return(
    <input 
     value={props.value}
    />
  )
}
Enter fullscreen mode Exit fullscreen mode

You will then use it in the Form component by passing a ref as a prop to the MyInput component. This will allow you to immediately focus the specific input field as soon as the Form renders. You could perform extra things like when a user enters an invalid value or attempts to submit values without filling a field, then you could scroll to and focus on that field, and perhaps change the border color to red. Refs open you up to special conditional customizations of your DOM elements.

Let's say you want to hide some functions or values from the Parent Form component. You could also want to create custom functions that bundle together different ref functions that the Form component can use. I will introduce you to a React hook called useImperativeHandler.

The hook is a way to control what properties and methods are accessible from a child component when accessed through a ref.

import React, { useRef, useImperativeHandle, forwardRef } from 'react';

const MyInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    getValue: () => {
      return inputRef.current.value;
    },
  }));

  return <input ref={inputRef} />;
});

const Form = () => {
  const childRef = useRef();

  const handleFocus = () => {
    // This will call the custom focus method in the child component
    childRef.current.focus(); 
  };

  const handleGetValue = () => {
    // This will call the custom getValue method in the child component
    const value = childRef.current.getValue(); 
    console.log('Value:', value);
  };

  return (
    <div>
      <MyInput ref={childRef} />
      <button onClick={handleFocus}>Focus Input</button>
      <button onClick={handleGetValue}>Get Value</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In this example, the Input component uses useImperativeHandle to customize the interface exposed through the ref. It exposes a focus method to focus on the input field and a getValue method to retrieve its value. The Form component can then use these methods through the childRef.

This approach allows you to encapsulate and control the interactions with a child component, exposing only the necessary functionality to the parent component through the ref.

Please refer to the following links for more in depth information regarding refs in React.

I am Zidane Gimiga, a full Stack software engineer and fervent Web3 enthusiast. I am committed to sharing different things I learn with others, mostly in software development. I hope you found value in this or had your interest piqued. Please feel free to ask questions or share your feedback. Until next time!

Top comments (0)