ReactJS - Refs and the DOM

Hello, future React developers! Today, we're going to dive into an exciting topic that will help you interact directly with DOM elements in your React applications. Buckle up, because we're about to explore the world of Refs and the DOM!

ReactJS - Refs and the DOM

What are Refs?

Before we jump into the nitty-gritty, let's understand what Refs are. In React, "Ref" is short for "reference". It's like giving a name tag to a specific element so you can easily find it later. Imagine you're at a big party, and you want to keep track of your best friend. You might give them a special hat or a unique name tag. That's essentially what a Ref does in React - it helps you keep track of specific elements.

Why do we need Refs?

You might be wondering, "Why can't we just use regular JavaScript to select elements?" Well, in React, we try to avoid directly manipulating the DOM (Document Object Model) because React has its own efficient way of updating the UI. However, there are times when we need to step outside of React's declarative paradigm and work directly with DOM elements. This is where Refs come in handy!

Creating Refs with createRef()

Let's start by looking at how we create a Ref in React. We use a method called createRef().

Signature of the createRef method

The signature of createRef() is quite simple:

React.createRef()

That's it! No parameters, no complications. It returns a plain object with a single property: current. Initially, this current property is set to null.

Let's see an example:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }

  render() {
    return <div ref={this.myRef} />;
  }
}

In this example, we create a Ref in the constructor and assign it to this.myRef. Then in the render method, we attach this Ref to a div element using the ref attribute.

Applying ref

Now that we've created our Ref, let's see how we can use it. There are a few ways to apply a Ref:

1. Refs on DOM Elements

The most common use case is to apply a Ref to a DOM element:

class AutoFocusInput extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

  componentDidMount() {
    this.inputRef.current.focus();
  }

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

In this example, we create a Ref for an input element. After the component mounts, we use the Ref to automatically focus on the input. The current property of the Ref gives us access to the actual DOM node.

2. Refs on Class Components

You can also use Refs with class components:

class AutoScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  componentDidUpdate() {
    const node = this.listRef.current;
    node.scrollToItem(0);
  }

  render() {
    return <MyListComponent ref={this.listRef} />;
  }
}

In this case, this.listRef.current will point to the instance of the MyListComponent class.

3. Refs and Functional Components

Functional components can't be given Refs directly, but you can use the forwardRef function to pass Refs through:

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.buttonRef = React.createRef();
  }

  render() {
    return <FancyButton ref={this.buttonRef}>Click me!</FancyButton>;
  }
}

In this example, FancyButton uses forwardRef to pass the Ref down to the actual button element.

Use cases of createRef

Now that we know how to create and apply Refs, let's look at some common use cases:

1. Managing focus, text selection, or media playback

class TextInput extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

  focusTextInput = () => {
    this.inputRef.current.focus();
  }

  render() {
    return (
      <div>
        <input type="text" ref={this.inputRef} />
        <button onClick={this.focusTextInput}>Focus the input</button>
      </div>
    );
  }
}

This component allows us to programmatically focus the input when the button is clicked.

2. Triggering imperative animations

class Animator extends React.Component {
  constructor(props) {
    super(props);
    this.elementRef = React.createRef();
  }

  animate = () => {
    this.elementRef.current.classList.add('animated');
  }

  render() {
    return (
      <div>
        <div ref={this.elementRef}>Animate me!</div>
        <button onClick={this.animate}>Start Animation</button>
      </div>
    );
  }
}

Here, we use a Ref to add a CSS class to an element, triggering an animation.

3. Integrating with third-party DOM libraries

class MapComponent extends React.Component {
  constructor(props) {
    super(props);
    this.mapRef = React.createRef();
  }

  componentDidMount() {
    const mapLibrary = new FancyMapLibrary();
    mapLibrary.createMap(this.mapRef.current);
  }

  render() {
    return <div ref={this.mapRef} style={{width: '100%', height: '400px'}} />;
  }
}

In this example, we use a Ref to give a third-party map library access to a DOM node where it can render a map.

Conclusion

Refs are a powerful tool in React that allow us to step outside the declarative paradigm when necessary. They provide a way to access DOM nodes or React elements directly. However, it's important to use Refs judiciously. In most cases, you can achieve what you need through React's declarative APIs. But when you do need that extra bit of control, Refs are there to help!

Remember, with great power comes great responsibility. Use Refs wisely, and your React applications will thank you!

Here's a quick reference table of the methods we've discussed:

Method Description Example
React.createRef() Creates a ref object this.myRef = React.createRef();
ref attribute Attaches a ref to a React element <div ref={this.myRef} />
forwardRef Allows functional components to receive refs const FancyButton = React.forwardRef((props, ref) => ...)

Happy coding, and may your Refs always be current!

Credits: Image by storyset