Mastering React Refs: Understanding useRef vs. Callback Refs
React provides two common ways to manage refs: useRef and callback refs. While they may seem similar at first glance, each has unique properties that make it more suitable for different scenarios. Let’s dive into both approaches and explore their differences with practical examples and performance insights.
What is useRef?
The useRef hook creates a mutable object with a .current property that persists across renders. This makes it useful for accessing DOM elements or storing mutable values without triggering re-renders.
Example: Tracking Input Focus with useRef
import React, { useRef } from 'react';
function InputFocusExample() {
const inputRef = useRef<HTMLInputElement>(null);
const handleFocus = () => {
inputRef.current?.focus(); // Access DOM node and focus on input
};
return (
<div>
<input ref={inputRef} type="text" placeholder="Click the button to focus me" />
<button onClick={handleFocus}>Focus Input</button>
</div>
);
}
export default InputFocusExample;
Explanation:
Callback Refs: What Are They?
Callback refs provide a more flexible way to manage refs by using a callback function. The function receives the DOM node as a parameter and can perform actions directly on it.
Example: Dynamic DOM Node Assignment with Callback Refs
import React, { useState } from 'react';
function CallbackRefExample() {
const [inputElement, setInputElement] = useState<HTMLInputElement | null>(null);
const handleFocus = () => {
inputElement?.focus(); // Focus the dynamically assigned input
};
return (
<div>
<input ref={(node) => setInputElement(node)} type="text" placeholder="Click the button to focus me" />
<button onClick={handleFocus}>Focus Input</button>
</div>
);
}
export default CallbackRefExample;
Explanation:
Recommended by LinkedIn
useRef vs. Callback Refs: Key Differences
Aspect useRef Callback Refs Usage Creates a persistent object with .current. Uses a function to dynamically set the DOM node. Reactivity Doesn’t trigger re-renders. Can trigger reactivity when combined with state. Flexibility Simple and straightforward. More flexible, allows dynamic node updates. Performance Lightweight and fast. Slightly more overhead due to function execution.
Improving Callback Refs with useCallback
One downside of using callback refs is that if the parent component re-renders frequently, a new function may be created each time. This can be mitigated by wrapping the callback in useCallback to memoize it.
Example: Memoized Callback Ref
import React, { useCallback, useState } from 'react';
function MemoizedCallbackRef() {
const [inputElement, setInputElement] = useState<HTMLInputElement | null>(null);
const setRef = useCallback((node: HTMLInputElement | null) => {
setInputElement(node); // Memoized function to set input element
}, []);
const handleFocus = () => {
inputElement?.focus();
};
return (
<div>
<input ref={setRef} type="text" placeholder="Click the button to focus me" />
<button onClick={handleFocus}>Focus Input</button>
</div>
);
}
export default MemoizedCallbackRef;
Benefit:
When to Use Each?
Conclusion
Both useRef and callback refs have their place in React development. By understanding their differences and use cases, you can write cleaner, more efficient code while avoiding unnecessary re-renders.