Mastering the React Ref API: Direct DOM Access the Right Way

Last updated 2 months, 2 weeks ago | 127 views 75     5

Tags:- React

Introduction: Why the React Ref API Matters

In React, we usually manage UI behavior declaratively using state and props. But sometimes, we need to directly access or manipulate a DOM element, such as:

  • Setting focus on an input field

  • Scrolling to a specific element

  • Controlling media playback (video/audio)

  • Integrating with third-party libraries (e.g., charts, sliders)

That’s where the React Ref API comes in.

The Ref (short for Reference) API lets you create a persistent reference to a DOM element or component instance—bypassing the need for document.querySelector() and keeping your logic React-friendly.


What Is the React Ref API?

React provides two main ways to create refs:

  • useRef() – The React Hook for functional components

  • createRef() – Used in class components

Refs do not trigger re-renders. They provide a mutable container whose .current property can store DOM nodes or any value.


⚙️ How to Use the React Ref API

1. Using useRef in Functional Components

import React, { useRef } from 'react';

function FocusInput() {
  const inputRef = useRef(null); // Create a ref object

  const handleClick = () => {
    inputRef.current.focus(); // Access the input element and focus it
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="Click the button to focus" />
      <button onClick={handleClick}>Focus Input</button>
    </div>
  );
}

✅ Explanation:

  • useRef(null) creates a reference object with .current = null

  • ref={inputRef} connects the ref to the DOM element

  • inputRef.current.focus() invokes the DOM method directly


2. Using createRef in Class Components

import React, { Component } from 'react';

class TextInput extends Component {
  constructor() {
    super();
    this.inputRef = React.createRef(); // Create a ref object
  }

  componentDidMount() {
    this.inputRef.current.focus(); // Focus input after mount
  }

  render() {
    return <input type="text" ref={this.inputRef} />;
  }
}

3. Using forwardRef to Pass Refs to Child Components

By default, you can’t pass refs to functional child components. You need forwardRef:

import React, { forwardRef, useRef } from 'react';

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

function ParentComponent() {
  const inputRef = useRef();

  return (
    <div>
      <CustomInput ref={inputRef} placeholder="Forwarded ref input" />
      <button onClick={() => inputRef.current.focus()}>Focus</button>
    </div>
  );
}

4. Storing Mutable Values (Not Just DOM Nodes)

const counter = useRef(0);

// Does NOT cause a re-render when updated
counter.current += 1;

Useful when you want to keep track of values across renders without triggering re-renders—like timers, previous values, or instance variables.


Complete Working Code Example

import React, { useRef } from 'react';

function RefExampleApp() {
  const inputRef = useRef(null);
  const clickCounter = useRef(0); // Store mutable value

  const handleClick = () => {
    inputRef.current.focus(); // Focus input
    clickCounter.current++;
    alert(`Input focused ${clickCounter.current} times`);
  };

  return (
    <div style={{ padding: "2rem" }}>
      <h2>React Ref API Example</h2>
      <input
        ref={inputRef}
        type="text"
        placeholder="Click the button to focus me"
        style={{ padding: "0.5rem", width: "300px" }}
      />
      <br />
      <button onClick={handleClick} style={{ marginTop: "1rem" }}>
        Focus & Count Clicks
      </button>
    </div>
  );
}

export default RefExampleApp;

✅ This demo shows:

  • Focusing an input using a ref

  • Persisting a counter without causing re-renders


Tips & Common Pitfalls

✅ Best Practices

  • Use useRef() for DOM manipulation, timers, and storing previous values.

  • Use forwardRef() to expose refs from custom components.

  • Use refs sparingly—prefer state and props for most interactions.

⚠️ Common Mistakes

  • Expecting changes to ref.current to trigger re-renders (they don’t).

  • Mutating ref.current during rendering—it should be done in event handlers or effects.

  • Forgetting to handle null when accessing ref.current.


Comparison: useRef vs createRef

Feature useRef() createRef()
Use case Functional components Class components
Re-creates on render ❌ No ✅ Yes (not persistent)
Triggers re-render ❌ No ❌ No
Mutability ✅ Yes ✅ Yes
When to use Preferable in modern React apps Legacy or class-based apps only

Conclusion: Use Refs the Right Way

The React Ref API is a powerful tool for bridging the gap between declarative React logic and imperative DOM manipulation.

Key Takeaways:

  • Use useRef() to access or persist values in functional components.

  • Only use refs when necessary—state is still the React way.

  • forwardRef() helps pass refs to child components.

  • Avoid relying on refs for business logic or rendering decisions.

Used wisely, refs give you more control without compromising React’s declarative nature. Start small—like focusing inputs or controlling animations—and expand as needed.