useImperativeHandle react hook

The useImperativeHandle hook in React is used to customize the instance value that is exposed when using ref in functional components. This hook is particularly useful when you want to expose imperative methods to parent components, allowing them to interact with child components in a more controlled way.

Simple Explanation

Imagine you have a child component with an input field, and you want the parent component to be able to focus or clear the input field. Normally, you would use ref to get access to the input element, but with useImperativeHandle, you can define exactly what methods or properties the parent can access.

Basic Usage

Here’s a simple example of how to use useImperativeHandle:

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

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

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

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

function ParentComponent() {
  const inputRef = useRef();

  return (
    <div>
      <MyInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus Input</button>
      <button onClick={() => inputRef.current.clear()}>Clear Input</button>
    </div>
  );
}

In this example, the MyInput component exposes focus and clear methods to the parent component.

Examples

  1. Custom Input with Focus and Clear Methods:
   import React, { useRef, useImperativeHandle, forwardRef } from 'react';

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

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

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

   function App() {
     const inputRef = useRef();

     return (
       <div>
         <CustomInput ref={inputRef} />
         <button onClick={() => inputRef.current.focus()}>Focus Input</button>
         <button onClick={() => inputRef.current.clear()}>Clear Input</button>
       </div>
     );
   }
  1. TextArea with Resize Method:
   import React, { useRef, useImperativeHandle, forwardRef } from 'react';

   const ResizableTextArea = forwardRef((props, ref) => {
     const textAreaRef = useRef();

     useImperativeHandle(ref, () => ({
       resize: (width, height) => {
         textAreaRef.current.style.width = width;
         textAreaRef.current.style.height = height;
       }
     }));

     return <textarea ref={textAreaRef} {...props} />;
   });

   function App() {
     const textAreaRef = useRef();

     return (
       <div>
         <ResizableTextArea ref={textAreaRef} />
         <button onClick={() => textAreaRef.current.resize('300px', '200px')}>Resize TextArea</button>
       </div>
     );
   }
  1. Custom Video Player Controls:
   import React, { useRef, useImperativeHandle, forwardRef } from 'react';

   const VideoPlayer = forwardRef((props, ref) => {
     const videoRef = useRef();

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

     return <video ref={videoRef} {...props} />;
   });

   function App() {
     const videoRef = useRef();

     return (
       <div>
         <VideoPlayer ref={videoRef} src="video.mp4" />
         <button onClick={() => videoRef.current.play()}>Play</button>
         <button onClick={() => videoRef.current.pause()}>Pause</button>
       </div>
     );
   }
  1. Custom Modal with Open and Close Methods:
   import React, { useRef, useImperativeHandle, forwardRef, useState } from 'react';

   const Modal = forwardRef((props, ref) => {
     const [isOpen, setIsOpen] = useState(false);

     useImperativeHandle(ref, () => ({
       open: () => setIsOpen(true),
       close: () => setIsOpen(false)
     }));

     if (!isOpen) return null;

     return (
       <div className="modal">
         <div className="modal-content">
           <span className="close" onClick={() => setIsOpen(false)}>&times;</span>
           {props.children}
         </div>
       </div>
     );
   });

   function App() {
     const modalRef = useRef();

     return (
       <div>
         <button onClick={() => modalRef.current.open()}>Open Modal</button>
         <button onClick={() => modalRef.current.close()}>Close Modal</button>
         <Modal ref={modalRef}>
           <h2>Modal Content</h2>
           <p>This is a modal.</p>
         </Modal>
       </div>
     );
   }
  1. Custom Slider with Set Value Method:
   import React, { useRef, useImperativeHandle, forwardRef } from 'react';

   const Slider = forwardRef((props, ref) => {
     const sliderRef = useRef();

     useImperativeHandle(ref, () => ({
       setValue: (value) => {
         sliderRef.current.value = value;
       }
     }));

     return <input type="range" ref={sliderRef} {...props} />;
   });

   function App() {
     const sliderRef = useRef();

     return (
       <div>
         <Slider ref={sliderRef} min="0" max="100" />
         <button onClick={() => sliderRef.current.setValue(50)}>Set Value to 50</button>
       </div>
     );
   }

Practice Exercises

  1. Create a custom input component that exposes a select method to select the text inside the input.
  2. Build a custom dropdown component that exposes methods to open and close the dropdown.
  3. Develop a custom timer component that exposes methods to start, stop, and reset the timer.
  4. Implement a custom carousel component that exposes methods to go to the next and previous slides.
  5. Create a custom tooltip component that exposes methods to show and hide the tooltip.

These exercises will help you get comfortable with using the useImperativeHandle hook in various scenarios.

Published
Categorized as React