Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
useImperativeHandle is a Hook that customizes the instance value exposed when using forwardRef. It lets you control exactly what parent components can access through a ref, creating clean and secure component APIs!
Parent has full access to DOM element
Parent only gets what you expose
With useImperativeHandle, you get:
First, create your component using forwardRef to accept refs from parent components.
forwardRef Required
useImperativeHandle only works with forwardRef components!
Add useImperativeHandle to define what methods and values the parent can access.
Custom API
Only focus() and clear() are exposed, not the input element!
Use the component in a parent and access only the methods you exposed.
Clean Access
Parent can only call focus() and clear(), nothing else!
Add dependencies to optimize when the imperative handle updates.
Performance Optimization
Handle only updates when dependencies change!
Create a component with a clean, controlled interface
const CustomInput = React.forwardRef((props, ref) => {
const inputRef = React.useRef();
const [value, setValue] = React.useState('');
const [isValid, setIsValid] = React.useState(true);
// Expose only specific methods to parent
React.useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
setValue('');
inputRef.current.value = '';
setIsValid(true);
},
getValue: () => value,
setValue: (newValue) => {
setValue(newValue);
inputRef.current.value = newValue;
validateInput(newValue);
},
isValid: () => isValid
}), [value, isValid]);
const validateInput = (inputValue) => {
const isValidEmail = /^[^s@]+@[^s@]+.[^s@]+$/.test(inputValue);
setIsValid(isValidEmail || inputValue === '');
};
const handleChange = (e) => {
const newValue = e.target.value;
setValue(newValue);
validateInput(newValue);
};
return (
<div className="custom-input">
<div className="input-wrapper">
<input
ref={inputRef}
type="email"
value={value}
onChange={handleChange}
placeholder="Enter your email..."
className={'input-field ' + (!isValid ? 'invalid' : '')}
/>
{!isValid && (
<div className="error-message">
Please enter a valid email address
</div>
)}
</div>
<div className="input-status">
Status: {isValid ? (value ? '✅ Valid' : '⏳ Empty') : '❌ Invalid'}
</div>
</div>
);
});
function CustomInputExample() {
const inputRef = React.useRef();
const [status, setStatus] = React.useState('');
const handleFocus = () => {
inputRef.current.focus();
setStatus('Input focused via imperative handle!');
};
const handleClear = () => {
inputRef.current.clear();
setStatus('Input cleared via imperative handle!');
};
const handleSetValue = () => {
inputRef.current.setValue('test@example.com');
setStatus('Value set via imperative handle!');
};
const handleGetValue = () => {
const value = inputRef.current.getValue();
setStatus(`Current value: "${value}"`);
};
const handleCheckValidity = () => {
const isValid = inputRef.current.isValid();
setStatus(`Input is valid: ${isValid ? '✅ Yes' : '❌ No'}`);
};
return (
<div className="container">
<h2>Custom Input with useImperativeHandle</h2>
<p>The input component exposes only specific methods - no direct DOM access!</p>
<div className="input-section">
<CustomInput ref={inputRef} />
</div>
<div className="controls-section">
<h3>Parent Controls (via Imperative Handle)</h3>
<div className="button-grid">
<button onClick={handleFocus} className="control-btn focus-btn">
🎯 Focus via Handle
</button>
<button onClick={handleClear} className="control-btn clear-btn">
🗑️ Clear via Handle
</button>
<button onClick={handleSetValue} className="control-btn set-btn">
📝 Set Value via Handle
</button>
<button onClick={handleGetValue} className="control-btn get-btn">
👁️ Get Value via Handle
</button>
<button onClick={handleCheckValidity} className="control-btn valid-btn">
✅ Check Validity via Handle
</button>
</div>
{status && (
<div className="status-message">
{status}
</div>
)}
</div>
<div className="info-section">
<h4>🛡️ What's Protected?</h4>
<ul>
<li>❌ Parent cannot access inputRef.current directly</li>
<li>❌ Parent cannot modify input.style</li>
<li>❌ Parent cannot change input.type</li>
<li>❌ Parent cannot access internal validation logic</li>
<li>✅ Parent can only call focus(), clear(), getValue(), setValue(), isValid()</li>
</ul>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<CustomInputExample />);Loading preview...
A working video player with controlled access
const VideoPlayer = React.forwardRef((props, ref) => {
const videoRef = React.useRef();
const [isPlaying, setIsPlaying] = React.useState(false);
const [currentTime, setCurrentTime] = React.useState(0);
const [duration, setDuration] = React.useState(0);
// Expose only specific methods to parent
React.useImperativeHandle(ref, () => ({
play: () => {
videoRef.current.play();
setIsPlaying(true);
},
pause: () => {
videoRef.current.pause();
setIsPlaying(false);
},
seekTo: (time) => {
videoRef.current.currentTime = time;
setCurrentTime(time);
},
getCurrentTime: () => currentTime,
getDuration: () => duration,
isPlaying: () => isPlaying,
setVolume: (volume) => {
videoRef.current.volume = Math.max(0, Math.min(1, volume));
}
}), [isPlaying, currentTime, duration]);
React.useEffect(() => {
const video = videoRef.current;
const handleTimeUpdate = () => {
setCurrentTime(video.currentTime);
};
const handleLoadedMetadata = () => {
setDuration(video.duration);
};
const handlePlay = () => setIsPlaying(true);
const handlePause = () => setIsPlaying(false);
video.addEventListener('timeupdate', handleTimeUpdate);
video.addEventListener('loadedmetadata', handleLoadedMetadata);
video.addEventListener('play', handlePlay);
video.addEventListener('pause', handlePause);
return () => {
video.removeEventListener('timeupdate', handleTimeUpdate);
video.removeEventListener('loadedmetadata', handleLoadedMetadata);
video.removeEventListener('play', handlePlay);
video.removeEventListener('pause', handlePause);
};
}, []);
const formatTime = (time) => {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
};
return (
<div className="video-player">
<video
ref={videoRef}
className="video-element"
controls={false}
preload="metadata"
muted
>
<source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
<div className="video-controls">
<div className="time-display">
{formatTime(currentTime)} / {formatTime(duration)}
</div>
<div className="video-status">
Status: {isPlaying ? '▶️ Playing' : '⏸️ Paused'}
</div>
</div>
</div>
);
});
function VideoPlayerExample() {
const videoRef = React.useRef();
const [status, setStatus] = React.useState('');
const handlePlay = () => {
videoRef.current.play();
setStatus('Video playing via imperative handle!');
};
const handlePause = () => {
videoRef.current.pause();
setStatus('Video paused via imperative handle!');
};
const handleSeek = () => {
videoRef.current.seekTo(5);
setStatus('Video seeked to 5 seconds via imperative handle!');
};
const handleInfo = () => {
const currentTime = videoRef.current.getCurrentTime();
const duration = videoRef.current.getDuration();
const playing = videoRef.current.isPlaying();
setStatus(`Time: ${Math.floor(currentTime)}s / ${Math.floor(duration)}s, Playing: ${playing}`);
};
const handleMute = () => {
videoRef.current.setVolume(0);
setStatus('Video muted via imperative handle!');
};
const handleUnmute = () => {
videoRef.current.setVolume(1);
setStatus('Video unmuted via imperative handle!');
};
return (
<div className="container">
<h2>Video Player with useImperativeHandle</h2>
<p>The video player exposes only specific methods - no direct DOM access!</p>
<div className="video-section">
<VideoPlayer ref={videoRef} />
</div>
<div className="controls-section">
<h3>Parent Controls (via Imperative Handle)</h3>
<div className="button-grid">
<button onClick={handlePlay} className="control-btn play-btn">
▶️ Play
</button>
<button onClick={handlePause} className="control-btn pause-btn">
⏸️ Pause
</button>
<button onClick={handleSeek} className="control-btn seek-btn">
⏭️ Seek to 5s
</button>
<button onClick={handleInfo} className="control-btn info-btn">
📊 Get Info
</button>
<button onClick={handleMute} className="control-btn mute-btn">
🔇 Mute
</button>
<button onClick={handleUnmute} className="control-btn unmute-btn">
🔊 Unmute
</button>
</div>
{status && (
<div className="status-message">
{status}
</div>
)}
</div>
<div className="info-section">
<h4>🛡️ What's Protected?</h4>
<ul>
<li>❌ Parent cannot access videoRef.current directly</li>
<li>❌ Parent cannot modify video.src</li>
<li>❌ Parent cannot change video.controls</li>
<li>❌ Parent cannot access video.load() method</li>
<li>✅ Parent can only call play(), pause(), seekTo(), getCurrentTime(), getDuration(), isPlaying(), setVolume()</li>
</ul>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<VideoPlayerExample />);Loading preview...
Video/audio players with play, pause, seek controls
Enhanced input components with focus, validation, formatting
Components with start, stop, reset animation controls
Basic components that don't need custom APIs
Use props or context for data, not imperative handles
Don't use for managing component state
Control exactly what parent components can access through refs.
Hide internal implementation details and protect DOM access.
Create clear, intentional APIs for your components.
Only works with components created using forwardRef.