Skip to content
intermediatePhase 36 · React

useRef

Use useRef for DOM references, mutable values, and avoiding re-renders.

30m
0 problems
Topic Progress0%

DOM References

DOM References

useRef creates a mutable reference that persists across renders.

Accessing DOM Elements

import { useRef, useEffect } from "react";

function TextInput() {
  const inputRef = useRef(null);

  useEffect(() => {
    // Focus input on mount
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} type="text" />;
}

ref Callback

function MeasureExample() {
  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });

  const measureRef = useCallback((node) => {
    if (node !== null) {
      setDimensions({
        width: node.offsetWidth,
        height: node.offsetHeight
      });
    }
  }, []);

  return (
    <div ref={measureRef}>
      <p>Width: {dimensions.width}</p>
      <p>Height: {dimensions.height}</p>
    </div>
  );
}

Multiple Refs

function Form() {
  const nameRef = useRef(null);
  const emailRef = useRef(null);
  const passwordRef = useRef(null);

  const focusName = () => nameRef.current.focus();
  const focusEmail = () => emailRef.current.focus();
  const focusPassword = () => passwordRef.current.focus();

  return (
    <form>
      <input ref={nameRef} placeholder="Name" />
      <input ref={emailRef} placeholder="Email" />
      <input ref={passwordRef} placeholder="Password" />
    </form>
  );
}

Forwarding Refs

const TextInput = forwardRef((props, ref) => {
  return <input ref={ref} {...props} />;
});

function Parent() {
  const inputRef = useRef(null);

  return (
    <div>
      <TextInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus</button>
    </div>
  );
}

Mutable Values

Mutable Values

useRef can store any mutable value without causing re-renders.

Storing Timer IDs

function Stopwatch() {
  const [time, setTime] = useState(0);
  const intervalRef = useRef(null);

  const start = () => {
    intervalRef.current = setInterval(() => {
      setTime((prev) => prev + 1);
    }, 1000);
  };

  const stop = () => {
    clearInterval(intervalRef.current);
  };

  return (
    <div>
      <p>Time: {time}s</p>
      <button onClick={start}>Start</button>
      <button onClick={stop}>Stop</button>
    </div>
  );
}

Previous Values

function Counter() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef(0);

  useEffect(() => {
    prevCountRef.current = count;
  }, [count]);

  return (
    <div>
      <p>Current: {count}</p>
      <p>Previous: {prevCountRef.current}</p>
    </div>
  );
}

Storing Values Without Re-render

function Component() {
  const renderCountRef = useRef(0);

  useEffect(() => {
    renderCountRef.current += 1;
  });

  // This value persists but doesn't cause re-renders
  return <p>Rendered {renderCountRef.current} times</p>;
}

Caching Values

function ExpensiveComponent({ data }) {
  const cachedResult = useRef(null);

  const result = useMemo(() => {
    // Expensive computation
    return computeExpensiveValue(data);
  }, [data]);

  // Store in ref for later use
  cachedResult.current = result;
}

Avoiding Re-renders

Avoiding Re-renders

When to Use useRef

  • Storing timer/interval IDs
  • Accessing DOM elements directly
  • Storing previous values
  • Caching expensive computations
  • Storing values that shouldn't trigger re-renders

useRef vs useState

// useState - causes re-render
const [count, setCount] = useState(0);

// useRef - no re-render
const countRef = useRef(0);

Example: Previous Value

function usePrevious(value) {
  const ref = useRef();

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}

// Usage
function Counter() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);

  return (
    <div>
      <p>Now: {count}, Before: {prevCount}</p>
    </div>
  );
}

Example: Storing Callbacks

function useInterval(callback, delay) {
  const savedCallback = useRef();

  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  useEffect(() => {
    const tick = () => savedCallback.current();
    const id = setInterval(tick, delay);
    return () => clearInterval(id);
  }, [delay]);
}

// Usage
function Timer() {
  const [count, setCount] = useState(0);

  useInterval(() => {
    setCount((c) => c + 1);
  }, 1000);
}

Practice Problems

0/3solved
Build useRef Component

Create a reusable React component implementing useRef. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
useRef Testing

Write unit and integration tests for useRef using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
useRef Performance

Optimize useRef for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What does useRef return?

Question 1 options

2. Does changing a ref cause a re-render?

Question 2 options

3. When should you use useRef?

Question 3 options

4. What is ref forwarding?

Question 4 options

Flashcards

Question

What does useRef return?

Answer

An object with a current property

Question

Does changing useRef cause re-renders?

Answer

No, it does not trigger re-renders

Question

What is ref forwarding?

Answer

Passing refs to child components using forwardRef

Question

What is useRef used for?

Answer

DOM access, timers, previous values, mutable storage

Question

What is useRef?

Answer

useRef is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.useRef returns an object with a current property
  • 2.Changing refs doesn't cause re-renders
  • 3.Use refs for DOM access and mutable values
  • 4.Use forwardRef to pass refs to children
  • 5.Refs persist across renders

Interview Tips

  • Explain useRef vs useState
  • Show how to access DOM elements
  • Demonstrate storing previous values

Cheat Sheet

Cheat Sheet

DOM Access

const inputRef = useRef(null);
<input ref={inputRef} />
inputRef.current.focus();

Mutable Storage

const timerRef = useRef(null);
timerRef.current = setInterval(...);

Previous Value

const prevRef = useRef();
useEffect(() => { prevRef.current = value; }, [value]);
return prevRef.current;

Forwarding Refs

const Comp = forwardRef((props, ref) => {
  return <input ref={ref} />;
});