useImperativeHandle Hook
useImperativeHandle Hook — control components via ref
In React, data typically flows from top to bottom via props. However, sometimes it's necessary for a parent component to directly call child methods or functions. For example: opening or closing a modal, scrolling a list, or resetting an input value. In such cases, React provides the useImperativeHandle hook to work with forwardRef.
useImperativeHandle(ref, createHandle, deps)
instructs React about what the parent should see when using a ref on a given component.
forwardRef.Let's create a component that contains an input and its own focus method. The parent component will be able to call this method via ref.
// CustomInput.js
import React, { useRef, forwardRef, useImperativeHandle } from 'react';
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = '';
}
}));
return (
<input
ref={inputRef}
type="text"
placeholder="Write something..."
/>
);
});
export default CustomInput;
// App.js
import React, { useRef } from 'react';
import CustomInput from './CustomInput';
export default function App() {
const inputRef = useRef();
return (
<div>
<CustomInput ref={inputRef} />
<br />
<button onClick={() => inputRef.current.focus()}>Focus</button>
<button onClick={() => inputRef.current.clear()}>Clear</button>
</div>
);
}
Here the parent (App) can call the child's (CustomInput) methods —
focus() and clear(), without passing props.
Thus, we created a small "public API" for the child.
Without useImperativeHandle, React would give the parent the DOM node via ref (e.g., reference to the input). But in many cases, that's not sufficient. Through useImperativeHandle, we can give the parent only the methods or data we want them to control.
This approach is especially useful when you want to limit the parent's access to internal logic.
This is a professional example where the parent opens and closes a modal via ref.
// Modal.js
import React, { useState, forwardRef, useImperativeHandle } 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 style={{
position: 'fixed',
top: 0, left: 0, right: 0, bottom: 0,
background: 'rgba(0,0,0,0.5)',
display: 'flex', justifyContent: 'center', alignItems: 'center'
}}>
<div style={{ background: 'white', padding: '20px', borderRadius: '8px' }}>
<h3>Modal Window</h3>
<button onClick={() => setIsOpen(false)}>Close</button>
</div>
</div>
);
});
export default Modal;
// App.js
import React, { useRef } from 'react';
import Modal from './Modal';
export default function App() {
const modalRef = useRef();
return (
<div>
<button onClick={() => modalRef.current.open()}>Open Modal</button>
<Modal ref={modalRef} />
</div>
);
}
In this example, the parent completely controls the child's behavior without props. It's very similar to the imperative "openModal()" or "closeModal()" function style.
Sometimes you want the parent to not be able to access all internal methods. useImperativeHandle allows you to "hide" the rest.
useImperativeHandle(ref, () => ({
open: () => setVisible(true),
close: () => setVisible(false)
}), []); // only two methods accessible to parent
This way, the parent cannot change state or DOM directly.
Write a component that: