useRef & Mutable References
EasyuseRef returns a mutable ref object whose .current property persists across renders without triggering re-renders. It serves two purposes: accessing DOM elements directly and storing mutable values that need to survive re-renders but should not cause them.
Interactive Visualization
Key Points
- Returns an object with a .current property that persists for the component lifetime
- Mutating .current does NOT trigger a re-render
- Use ref attribute on JSX elements to access the underlying DOM node
- Common for focus management, measuring dimensions, and storing interval IDs
- Can store any mutable value like a class instance field
- Refs are the escape hatch from React's declarative model to imperative DOM access
Code Examples
DOM Access and Focus
import { useRef, useEffect } from 'react' function SearchInput() { const inputRef = useRef<HTMLInputElement>(null) useEffect(() => { inputRef.current?.focus() }, []) return <input ref={inputRef} type="text" placeholder="Search..." /> }
The ref attribute connects the JSX element to the ref object, giving direct access to the DOM node for imperative operations like focusing
Storing Mutable Values
import { useRef, useState, useCallback } from 'react' function Stopwatch() { const [elapsed, setElapsed] = useState(0) const intervalRef = useRef<number | null>(null) const startTimeRef = useRef<number>(0) const start = useCallback(() => { startTimeRef.current = Date.now() - elapsed intervalRef.current = window.setInterval(() => { setElapsed(Date.now() - startTimeRef.current) }, 10) }, [elapsed]) const stop = useCallback(() => { if (intervalRef.current !== null) { clearInterval(intervalRef.current) intervalRef.current = null } }, []) const reset = useCallback(() => { stop() setElapsed(0) }, [stop]) return ( <div> <p>{(elapsed / 1000).toFixed(2)}s</p> <button onClick={start}>Start</button> <button onClick={stop}>Stop</button> <button onClick={reset}>Reset</button> </div> ) }
Interval IDs and timestamps are stored in refs because they need to persist across renders but changes should not trigger re-renders
Previous Value Tracking
import { useRef, useEffect } from 'react' function usePrevious<T>(value: T): T | undefined { const ref = useRef<T | undefined>(undefined) useEffect(() => { ref.current = value }) return ref.current } function PriceDisplay({ price }: { price: number }) { const prevPrice = usePrevious(price) const direction = prevPrice !== undefined ? price > prevPrice ? 'up' : price < prevPrice ? 'down' : 'same' : 'same' return <span className={direction}>{price}</span> }
Refs can track previous render values because the effect updates .current after render, returning the old value during the current render
Common Mistakes
- Reading or writing ref.current during render, which makes the component impure
- Using refs to store values that should cause re-renders when changed
- Forgetting that ref.current is null until the component mounts and the DOM element is created
Interview Tips
- Explain the difference between useRef and useState for persisting values
- Know when refs are appropriate vs when state is more correct
- Discuss forwardRef for passing refs through component boundaries