React hooks - useImperativeHandle

Introduction

useImperativeHandle is one of those hooks that developers don't usually use but it has its rare use cases that we will go through in this blog and try to understand how it is used.

The useImperativeHandle Hook

If you have been working with React for long enough, then you must be aware of the fact that you can't pass a ref object down to a custom component. If you are new to React, then no need to feel left out. At least you now know something new about the ref object returned by the useRef hook. But if you aren't aware of what the useRef hook does be sure to check out my blog on useRef.

For a ref object to be passed down directly to the custom-created component, the component itself has to be wrapped in React.forwardRef. This is done where the component is defined in your project folder. Take a look at the example below to know how it is done.

import { useRef, useState } from "react";

export default function App() {
    const [ open, setOpen ] = useState(false);
    const inputRef = useRef();

    return (
        <>
           <button onClick={() => setOpen(true)}>Open Modal</button>
            {
                open ? 
                <button onClick={() => }>Focus input</button>
                :
                null
            }
            <CustomInput
                ref={inputRef}
                open={open}
                onClose={() => setOpen(false)}
            />
        </>
    );
}
import { useState, forwardRef } from "react";

function CustomInput({ open, onClose }, ref) {
    const [ data, setData ] = useState("");
     return (
        <div>
            <h1>Write Anything</h1>
            <input ref={ref} value={data} onChange={() => setData(e.target.value)} placeholder="Write Anything" />
        </div>
     );   
}

export default forwardRef(CustomInput);

Now that you have seen the example and have an understanding of how to pass down a ref to a custom-created component, let us now look at a scenario where we would need the useImperativeHandle hook.

But before that let us understand what useImperativeHandle does essentially.

useImperativeHandle allows us to override a ref passed down to a custom-created component and lets us return a new custom-created value for the ref which is returned by the component. Hmmm...... If that doesn't make sense, don't worry. We will take a look at an example.

But before that, there's something else that you need to know.

useImperativeHandle takes in 3 parameters, the third being optional, the first is the ref that is to be overridden, and the second is a function that returns the new value of the ref.

Now let us take the same example above and make a few changes to it.

import { useRef, useState } from "react";
import CustomInput from "./components/CustomInput"; //Import the CustomInput component into the App.js 

export default function App() {
    const [ open, setOpen ] = useState(false);
    const inputRef = useRef();

    return (
        <>
           <button onClick={() => setOpen(true)}>Open Modal</button>
           <button onClick={() => inputRef.current.alertHi()}>CLick ME!</button>
            {
                open ? 
                <button onClick={() => inputRef.current.focusRefBtn.focus()}>Focus input</button>
                :
                null
            }
            <CustomInput
                ref={inputRef}
                open={open}
                onClose={() => setOpen(false)}
            />
        </>
    );
}
import { useState, forwardRef, useRef, useImperativeHandle } from "react";

function CustomInput({ open, onClose }, ref) {
    const [ data, setData ] = useState("");
    const focusRef = useRef();

    useImperativeHandle(ref, () => {
        return { 
            focusRefBtn: focusRef.current,
            alertHi: () => alert("Hi"),
        }
    }, [ open ]);

    if(!open) return null;

    return (
        <div ref={ref}>
            <button onClick={onClose}>Close</button>
            <h1>Write Anything</h1>
            <input ref={focusRef} value={data} onChange={(event) => setData(event.target.value)} placeholder="Write Anything" />
        </div>
     );   
}

export default forwardRef(CustomInput);

The GIF above is basically what the example is all about. We are accessing the custom ref value created in the CustomInput component in the App.js file and using it to focus on the input element returned by the CustomInput component.

I have attached the link to my GitHub repo for the above example here. You can pull the contents and run the example on your local computer.

Final Thoughts

Hope the blog served its purpose and assisted you in learning about the useImperativeHandle hook. If you still have any doubts, feel free to shoot them down in the comments. I will see you in the next blog. Till then stay safe, be happy, and code furiously!

You can follow me on Github, or LinkedIn.

Thank you for taking the time to read the blog.