Skip to main content
Practice Problems

Refs in React (useRef, createref, forwardref)

What are Refs?

Refs (references) are a way to get direct access to DOM elements or React components from code.

Refs are used when you need to:

  • Manage focus, text selection
  • Trigger animations
  • Integrate with third-party libraries
  • Measure element dimensions

useRef

useRef is a hook for creating refs in functional components.

Accessing DOM elements

javascript
import { useRef } from 'react'; function TextInput() { const inputRef = useRef(null); function handleClick() { // Get direct access to input inputRef.current.focus(); } return ( <div> <input ref={inputRef} type="text" /> <button onClick={handleClick}>Focus Input</button> </div> ); }

Storing mutable value

useRef can be used to store any value that doesn't trigger re-render:

javascript
function Timer() { const [count, setCount] = useState(0); const intervalRef = useRef(null); function start() { if (intervalRef.current) return; intervalRef.current = setInterval(() => { setCount(c => c + 1); }, 1000); } function 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> ); }

useRef vs useState

javascript
// useState - triggers re-render const [value, setValue] = useState(0); // useRef - does NOT trigger re-render const valueRef = useRef(0); valueRef.current = 1; // Won't trigger render

createRef

createRef is used in class components:

javascript
class TextInput extends React.Component { constructor(props) { super(props); this.inputRef = React.createRef(); } handleClick = () => { this.inputRef.current.focus(); }; render() { return ( <div> <input ref={this.inputRef} type="text" /> <button onClick={this.handleClick}>Focus</button> </div> ); } }

Important:

Don't use createRef in functional components! A new ref will be created on every render. Use useRef.


forwardRef

forwardRef allows passing a ref through a component to its child.

Problem

javascript
function CustomInput(props) { return <input {...props} />; } // Doesn't work! ref is not forwarded function Parent() { const inputRef = useRef(null); return <CustomInput ref={inputRef} />; // Error! }

Solution

javascript
const CustomInput = forwardRef((props, ref) => { return <input ref={ref} {...props} />; }); function Parent() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <div> <CustomInput ref={inputRef} /> <button onClick={handleClick}>Focus</button> </div> ); }

useImperativeHandle

useImperativeHandle lets you customize what's accessible to the parent through ref.

Without useImperativeHandle

javascript
const CustomInput = forwardRef((props, ref) => { return <input ref={ref} />; }); // Parent gets access to the entire DOM input

With useImperativeHandle

javascript
import { forwardRef, useImperativeHandle, useRef } from 'react'; const CustomInput = forwardRef((props, ref) => { const inputRef = useRef(null); useImperativeHandle(ref, () => ({ // Only expose these methods focus: () => { inputRef.current.focus(); }, scrollIntoView: () => { inputRef.current.scrollIntoView(); } // value, blur and other methods are not accessible })); return <input ref={inputRef} {...props} />; }); function Parent() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); // Works // inputRef.current.value; // undefined! } return ( <div> <CustomInput ref={inputRef} /> <button onClick={handleClick}>Focus</button> </div> ); }

Practical examples

Autofocus on mount

javascript
function AutoFocusInput() { const inputRef = useRef(null); useEffect(() => { inputRef.current.focus(); }, []); return <input ref={inputRef} />; }

Measuring element dimensions

javascript
function MeasureComponent() { const divRef = useRef(null); const [dimensions, setDimensions] = useState({}); useEffect(() => { if (divRef.current) { const { width, height } = divRef.current.getBoundingClientRect(); setDimensions({ width, height }); } }, []); return ( <div> <div ref={divRef} style={{ padding: 20, backgroundColor: 'lightblue' }}> Measure me </div> <p>Width: {dimensions.width}px</p> <p>Height: {dimensions.height}px</p> </div> ); }

Third-party library integration

javascript
function VideoPlayer({ src }) { const videoRef = useRef(null); useEffect(() => { // Initialize player with third-party library const player = new ThirdPartyPlayer(videoRef.current); player.load(src); return () => { player.destroy(); }; }, [src]); return <video ref={videoRef} />; }

Previous value

javascript
function usePrevious(value) { const ref = useRef(); useEffect(() => { ref.current = value; }, [value]); return ref.current; } 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> ); }

Callback Refs

Alternative way to work with refs using callback function:

javascript
function Component() { const [height, setHeight] = useState(0); const measuredRef = useCallback(node => { if (node !== null) { setHeight(node.getBoundingClientRect().height); } }, []); return ( <div> <div ref={measuredRef}> <p>This div's height is {height}px</p> </div> </div> ); }

When NOT to use Refs

Don't use for what can be done declaratively

javascript
// Bad function Dialog() { const dialogRef = useRef(null); function open() { dialogRef.current.style.display = 'block'; } function close() { dialogRef.current.style.display = 'none'; } return <div ref={dialogRef}>Dialog</div>; } // Good function Dialog() { const [isOpen, setIsOpen] = useState(false); return isOpen ? <div>Dialog</div> : null; }

Don't store data that affects rendering

javascript
// Bad function Component() { const dataRef = useRef([]); function addItem(item) { dataRef.current.push(item); // Component won't re-render! } return <div>{dataRef.current.length} items</div>; } // Good function Component() { const [data, setData] = useState([]); function addItem(item) { setData(prev => [...prev, item]); // Re-render will happen } return <div>{data.length} items</div>; }

TypeScript typing

typescript
import { useRef, forwardRef, useImperativeHandle } from 'react'; // useRef with DOM element function Component() { const inputRef = useRef<HTMLInputElement>(null); function handleClick() { inputRef.current?.focus(); } return <input ref={inputRef} />; } // forwardRef interface Props { placeholder?: string; } const CustomInput = forwardRef<HTMLInputElement, Props>((props, ref) => { return <input ref={ref} {...props} />; }); // useImperativeHandle interface CustomInputHandle { focus: () => void; reset: () => void; } const CustomInput = forwardRef<CustomInputHandle, Props>((props, ref) => { const inputRef = useRef<HTMLInputElement>(null); useImperativeHandle(ref, () => ({ focus() { inputRef.current?.focus(); }, reset() { if (inputRef.current) { inputRef.current.value = ''; } } })); return <input ref={inputRef} {...props} />; });

Common mistakes

Accessing ref.current before mounting

javascript
// Wrong function Component() { const ref = useRef(null); console.log(ref.current); // null! Element not created yet return <div ref={ref}>Hello</div>; } // Correct function Component() { const ref = useRef(null); useEffect(() => { console.log(ref.current); // Element is accessible }, []); return <div ref={ref}>Hello</div>; }

Creating new ref on every render

javascript
// Wrong function Component() { const ref = createRef(); // New ref on every render! return <div ref={ref}>Hello</div>; } // Correct function Component() { const ref = useRef(null); // Same ref return <div ref={ref}>Hello</div>; }

Conclusion

Refs in React:

  • useRef — for functional components
  • createRef — for class components
  • forwardRef — for forwarding ref through component
  • useImperativeHandle — for controlling ref access
  • Don't trigger re-render on change
  • Use when declarative approach is impossible
  • Avoid for what can be done through state

In interviews:

Important to be able to:

  • Explain what refs are and when to use them
  • Show difference between useRef and useState
  • Explain what forwardRef and useImperativeHandle are for
  • Give examples of proper ref usage
  • Describe when NOT to use refs

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems