Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
useRef is a Hook that returns a mutable ref object whose .current property persists for the full lifetime of the component. It's perfect for accessing DOM elements directly and storing values that don't trigger re-renders.
State updates cause re-renders even when you just need to track something internally!
State doesn't provide direct access to DOM elements for manipulation!
Regular variables are reset on every render, making cleanup impossible!
Ref updates don't trigger re-renders, perfect for internal tracking!
Refs provide direct access to DOM elements for manipulation!
Ref values persist across renders, enabling proper cleanup!
Initialize a ref with useRef() and an initial value. Think of it as creating a special container that persists across renders.
Pro Tip
Use null for DOM elements, 0 for numbers, or objects for complex data.
Use the .current property to read and write the ref value. This is your gateway to the stored data.
Key Point
The ref object is just a container. Always use .current to access the actual value!
Attach the ref to JSX elements using the ref attribute. This gives you direct access to DOM elements!
Safety First
Use optional chaining ?. when accessing DOM refs. They're null before mount and after unmount!
Modify ref values without triggering re-renders. This is perfect for performance optimization and internal tracking!
Performance Boost
Ref updates are synchronous and don't trigger re-renders. Perfect for tracking values internally without UI updates!
Use refs to store values needed for cleanup. This prevents memory leaks and ensures proper resource management!
Memory Management
Refs are essential for storing timers, event listeners, and other resources that need cleanup. Prevent memory leaks!
Auto-focus and form navigation with refs
function FocusForm() {
const nameRef = React.useRef(null);
const emailRef = React.useRef(null);
const messageRef = React.useRef(null);
const handleNameEnter = (e) => {
if (e.key === 'Enter') {
e.preventDefault();
emailRef.current?.focus();
}
};
const handleEmailEnter = (e) => {
if (e.key === 'Enter') {
e.preventDefault();
messageRef.current?.focus();
}
};
const handleSubmit = (e) => {
e.preventDefault();
alert('Form submitted!');
nameRef.current?.focus();
};
return (
<div className="focus-form">
<h2>Contact Form</h2>
<p>Press Enter to move to next field</p>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>Name:</label>
<input
ref={nameRef}
type="text"
placeholder="Enter your name"
onKeyDown={handleNameEnter}
autoFocus
/>
</div>
<div className="form-group">
<label>Email:</label>
<input
ref={emailRef}
type="email"
placeholder="Enter your email"
onKeyDown={handleEmailEnter}
/>
</div>
<div className="form-group">
<label>Message:</label>
<textarea
ref={messageRef}
placeholder="Enter your message"
rows={4}
/>
</div>
<div className="button-group">
<button type="button" onClick={() => nameRef.current?.focus()}>
Reset Focus
</button>
<button type="submit">Submit</button>
</div>
</form>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<FocusForm />);Loading preview...
Track renders without causing re-renders
function RenderTracker() {
const renderCountRef = React.useRef(0);
const lastRenderRef = React.useRef(Date.now());
const [count, setCount] = React.useState(0);
const [text, setText] = React.useState('');
// Track renders without causing re-renders
renderCountRef.current += 1;
lastRenderRef.current = Date.now();
const expensiveCalculation = React.useCallback(() => {
console.log('Expensive calculation running...');
return Array.from({ length: 1000000 }, (_, i) => i * 2).reduce((a, b) => a + b, 0);
}, []);
const handleExpensiveOperation = React.useCallback(() => {
const result = expensiveCalculation();
alert('Calculation result: ' + result);
}, [expensiveCalculation]);
return (
<div className="tracker">
<h2>Render Performance Tracker</h2>
<div className="stats">
<div className="stat">
<span className="label">Render Count:</span>
<span className="value">{renderCountRef.current}</span>
</div>
<div className="stat">
<span className="label">Last Render:</span>
<span className="value">{new Date(lastRenderRef.current).toLocaleTimeString()}</span>
</div>
</div>
<div className="controls">
<div className="control-group">
<label>State Count: {count}</label>
<button onClick={() => setCount(c => c + 1)}>
Increment State (Triggers Re-render)
</button>
</div>
<div className="control-group">
<label>Input Text:</label>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Type to trigger re-renders"
/>
</div>
<div className="control-group">
<button onClick={handleExpensiveOperation}>
Run Expensive Calculation
</button>
</div>
</div>
<div className="info">
<p>💡 Render count is tracked with useRef - it updates without causing additional re-renders!</p>
<p>Try typing in the input and watch how render count increases.</p>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<RenderTracker />);Loading preview...
Use state for values that need to trigger re-renders and update the UI. Use refs only for internal tracking or DOM access.
Always check if DOM refs exist before accessing them. Use optional chaining or conditional checks.
Create refs at the component level, not inside loops or conditional rendering. For multiple elements, use an array or object to store refs.
Always return a cleanup function from useEffect to clear intervals, event listeners, or other resources stored in refs.
Ref updates don't trigger component re-renders, perfect for performance optimization.
Direct access to DOM elements enables focus management, scrolling, and media control.
Ref values persist across renders without being reset, essential for cleanup operations.
Track renders, store previous values, and optimize expensive operations without UI updates.