Skip to main content

useRef and useImperativeHandle

useRef - DOM Access and Mutable Values

useRef creates a mutable reference that persists across renders without causing re-renders when changed.

DOM References

function TextInput() {
const inputRef = useRef<HTMLInputElement>(null);

const focusInput = () => {
inputRef.current?.focus();
};

return (
<>
<input ref={inputRef} type='text' />
<button onClick={focusInput}>Focus Input</button>
</>
);
}

Storing Mutable Values

function Timer() {
const [count, setCount] = useState(0);
const intervalRef = useRef<number | null>(null);

const start = () => {
if (intervalRef.current !== null) return;

intervalRef.current = setInterval(() => {
setCount(c => c + 1);
}, 1000);
};

const stop = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};

useEffect(() => {
return () => stop(); // Cleanup
}, []);

return (
<div>
<p>Count: {count}</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
);
}

Previous Value Tracking

function usePrevious\<T\>(value: T): T | undefined {
const ref = useRef\<T\>();

useEffect(() => {
ref.current = value;
}, [value]);

return ref.current;
}

// Usage
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);

return (
<div>
<p>Current: {count}</p>
<p>Previous: {prevCount}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}

useImperativeHandle - Custom Ref API

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

interface VideoPlayerHandle {
play: () => void;
pause: () => void;
reset: () => void;
}

const VideoPlayer = forwardRef<VideoPlayerHandle>((props, ref) => {
const videoRef = useRef<HTMLVideoElement>(null);

useImperativeHandle(ref, () => ({
play() {
videoRef.current?.play();
},
pause() {
videoRef.current?.pause();
},
reset() {
if (videoRef.current) {
videoRef.current.currentTime = 0;
videoRef.current.pause();
}
},
}));

return <video ref={videoRef} src='video.mp4' />;
});

// Usage
function App() {
const playerRef = useRef<VideoPlayerHandle>(null);

return (
<>
<VideoPlayer ref={playerRef} />
<button onClick={() => playerRef.current?.play()}>Play</button>
<button onClick={() => playerRef.current?.pause()}>Pause</button>
<button onClick={() => playerRef.current?.reset()}>Reset</button>
</>
);
}

Next Steps