In React, useRef
stores a reference to a value that can be mutated without causing re-rendering of the component.
While the value can be mutated, it should not occur during the rendering phase. Instead, mutations should happen in response to user actions or state changes, or inside a useEffect
hook or inside some event listener or something else.
It is commonly used for directly manipulating the DOM, by using the reference to an element.
Example:
const StopWatch = () => {
const [startTime, setStartTime] = useState<number>(0);
const intervalRef = useRef<number>(0);
const [endTime, setEndTime] = useState<number>(0);
const onStartClick = () => {
setStartTime(Date.now());
clearInterval(intervalRef.current);
const interval = setInterval(() => {
setEndTime(Date.now());
}, 10);
intervalRef.current= interval;
}
const onStopClick = () => {
clearInterval(intervalRef.current);
setStartTime(0);
setEndTime(0);
intervalRef.current = null;
}
return (
<div>
<p>{(endTime-startTime)/1000}</p>
<button onClick={onStartClick} disabled={endTime > 0}>Start</button>
<button onClick={onStopClick} disabled={endTime <= 0}>Stop</button>
</div>
)
}
In this example, the stopwatch timer is controlled with useRef
for handling the interval.
Passing ref
to Child Components with forwardRef
In React versions prior to 19, forwardRef
was used for passing a ref
to a child component from parent. This gave parent access to child DOM and its nodes and perform whatsoever action it want to perform.
Example:
const Form = () => {
const ref = useRef<HTMLInputElement>(null);
const handleClick = () => {
if (ref) {
ref.current?.focus();
}
}
return (
<>
<Input ref={ref} />
<button onClick={handleClick}>Focus</button>
</>
);
}
const Input = forwardRef(function (props, ref) {
return <input ref={ref} />
});
forwardRef
takes two parameter one is props
and other is ref
.
In our example Form
is parent and Input
is child. You can see onClicking button in parent the input element is focussed.
In React 19, we can do straight away without forwardRef by passing ref as props of child component
// Parent will be as it is only child will change
const Input = ({ref}) => (<input ref={ref} />);
Controlling What Gets Exposed with useImperativeHandle
When you use ref
in React to reference a element, it can expose the entire DOM node.This might not always be desirable, especially when you only want to expose specific methods or functionality. To solve this problem, useImperativeHandle
is used when we passed ref
from parent to child.
const Input = forwardRef(function (props, ref) {
const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current?.focus();
}
}
}, [])
return (<input ref={inputRef} />);
});
const Form = () => {
const ref = useRef<HTMLInputElement>(null);
const handleClick = () => {
if (ref) {
// This will only have access to focus from child and nothing else
ref.current?.focus();
}
}
return (
<>
<Input ref={ref} />
<button onClick={handleClick}>Focus</button>
</>
);
}
const Input = forwardRef(function (props, ref) {
return <input ref={ref} />
});
This blog explores the use of useRef
in React for efficient DOM manipulation, highlighting how to pass refs to child components (including the new approach in React 19), and control which methods or properties are exposed using useImperativeHandle