Delving Developer

Harness the Power of useImperativeHandle in React

Eddie Cunningham
Eddie Cunningham
4 min readReact.js
Cover Image for Harness the Power of useImperativeHandle in React

Introductionlink

React is a vast library that provides various features for building user interfaces. React embraces a modular approach and offers life cycles, hooks, libraries, and other tools to help developers create reusable, modular and testable components.

In this article, we'll introduce one of React's built-in hooks: useImperativeHandle. We will explore its properties, how to create custom hooks with it, and provide real-world examples to demonstrate its use cases.

What is useImperativeHandle in React?link

The useImperativeHandle hook in React enables the exposure of a child component's functions to its parent. Suppose you have a child component that possesses a custom feature - and you want to utilize it in the parent component, useImperativeHandle makes it possible.

This hook only works with components created by React.forwardRef, where the child component exposes its ref attribute to useImperativeHandle and allows it to navigate through the DOM to reach the child components' functions and properties.

Here is an example of using useImperativeHandle with forwardRef:

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

const ChildComponent = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    customFunction: () => {
      console.log('Do something');
    },
  }));

  return <div>I am a child component</div>;
});

Here, useImperativeHandle allows the child component, ChildComponent, to expose its customFunction to the parent component.

Creating Custom Hooks with useImperativeHandlelink

A notable feature of useImperativeHandle is its ability to build powerful and reusable custom hooks. When you create a custom hook using useImperativeHandle, it becomes easy to distribute and reuse relevant parts of your code.

Here is an example of creating a custom hook using useImperativeHandle:

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

const useCustomHook = (initialValue) => {
  const ref = useRef();

  useImperativeHandle(ref, () => ({
    customFunction: () => {
      console.log('Do something');
    },
  }));

  return [initialValue, ref];
};

const ParentComponent = () => {
  const childRef = useRef();
  const [value, setValue] = useCustomHook('initial value');

  const handleClick = () => {
    childRef.current.customFunction();
  };

  return (
    <>
      <button onClick={handleClick}>Call Child Function</button>
      <ChildComponent ref={childRef} />
    </>
  );
};

In this example, we defined a custom hook named useCustomHook. It exposes value and ref to the parent component.

Real-World Exampleslink

Let's go through real-world scenarios in which we could use useImperativeHandle.

Custom Validation Hooks

We can create custom validation hooks that include a ref to the child components, allowing for the call of a child component's validate() function from the parent component.

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

export const useInputValidation = () => {
  const ref = useRef(null);

  useImperativeHandle(ref, () => ({
    validate: () => {
      const val = ref.current.value;
      const regex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
      const isValid = regex.test(val);
      return isValid ? null : 'Invalid Email Address';
    },
  }));

  return ref;
};

const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef(null);
  const handleChange = (e) => {
    e.preventDefault();
    props.onChange(e.target.value);
  });

  return (
    <<form>
      <input type="text" ref={inputRef} />
    </form>
  );
};

const ParentComponent = () => {
  const childRef = useRef(null);
  const inputRef = useInputValidation();

  const handleSubmit = () => {
    const validationMessage = childRef.current.validate();
    if (validationMessage) {
      alert(validationMessage);
    } else {
      alert('Submit form!');
    }
  };

  return (
    <>
      <ChildComponent ref={childRef} />
      <button onClick={handleSubmit}>Submit</button>
    </>
  );
};

In this example, we have created a custom validation hook useInputValidation. We then have a ChildComponent add a ref to its input field, which the parent then uses to check the input value's validity.

Interactive Child Components

You can create interactive child components using useImperativeHandle. For instance, let's consider a Video component that has a play function, which starts the video at its last playback position. The Video component can then expose the play function to a parent component, where the playAgain function can call the child component's play function when a Replay button is clicked.

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

const Video = forwardRef((props, ref) => {
  const videoRef = useRef(null);
  const lastPosition = useRef(0);

  useImperativeHandle(ref, () => ({
    play: () => {
      videoRef.current.currentTime = lastPosition.current;
      videoRef.current.play();
    },
  }));

  const handleEnded = () => {
    lastPosition.current = 0;
  };

  const handlePause = () => {
    lastPosition.current = videoRef.current.currentTime;
  };

  return (
    <video
      ref={videoRef}
      src={props.src}
      onEnded={handleEnded}
      onPause={handlePause}
    />
  );
});

const ParentComponent = () => {
  const videoRef = useRef(null);

  const handleReplay = () => {
    videoRef.current.play();
  };

  return (
    <>
      <Video src="/src/video.mp4" ref={videoRef} />
      <button onClick={handleReplay}>Replay</button>
    </>
  );
};

Here, we have a Video component that saves the video's last position when it pauses or ends. We can then expose the play function to the parent component using useImperativeHandle.

In conclusion, useImperativeHandle is a powerful hook in React that enables the creation of clean, reusable, and modular code. We previously defined the paradigm of custom hooks and provided examples of two real-world scenarios in which this hook's usage becomes useful. As developers continue to hone their craft at React, they should consider leveraging useImperativeHandle to gain the best possible results from their code.