A Guide for Refs in React

A Guide for Refs in React

Learn how, why, and when to utilize React refs to access the DOM through the interface the library exposes. We’re going to investigate why React, a framework meant to abstract your code away from DOM manipulation, leaves the door open for developers to access it. This tutorial explains why it's important to only use refs when there is an implicit function call React can't handle through its methods.

In this article, we’re going to investigate why React, a framework meant to abstract your code away from DOM manipulation, leaves the door open for developers to access it.

As is the case with many other UI libraries, React offers a way to rethink a view as the result of a state of a component.

This is a big pivot away from how we usually build applications.

When we become familiar with some of these new concepts, we discover how easy it is to solve simple problems in the frontend world that used to cause us some trouble.

Part of that benefit comes from creating the views with the abstraction mechanisms React and JSX expose instead of doing it through DOM spec methods.

Still, the React team did something smart that all library authors should do: they provided escape hatches and kept the library open for situations beyond the ones they were specifically designed for, as well as situations the model may not work for.

Creating refs

As I said, refs are escape hatches for React developers, and we should try to avoid using them if possible.

When we obtain a node by using a ref and later modify some attribute or the DOM structure of it, it can enter into conflict with React’s diff and update approaches.

We’re going to cover anti-patterns later in this article. First, let’s start with a simple component and grab a node element using refs.

import React from 'react'

class ActionButton extends React.Component {

  render() {
    const { label, action } = this.props
    return (
      <button onClick={action}>{label}</button>
    )
  }
}

The <button> expression here is actually the JSX way of calling the React.createElement('button') statement, which is not actually a representation of an HTML Button element — it’s a React element.

You can gain access to the actual HTML element by creating a React reference and passing it to the element itself.

import React, { createRef } from 'react'

class ActionButton extends React.Component {

  constructor() {
    super()
    this.buttonRef = createRef()
  }

  render() {
    const { label, action } = this.props
    return (
      <button onClick={action} ref={this.buttonRef}>{label}</button>
    )
  }
}

This way, at any time in the lifecycle of the component, we can access the actual HTML element at this.buttonRef.current.

But what about functions that act as components?

Recently, the React team released Hooks to pair them with the same features class components have.

We can now import useRef for refs inside function components as well.

import React, { useRef } from 'react'

function ActionButton({ label, action }) {
    const buttonRef = useRef(null)

    return (
      <button onClick={action} ref={buttonRef}>{label}</button>
    )
  }
}

We know how to access DOM nodes inside a React component. Let’s take a look at some of the situations where this may be useful.

Usage of React refs

One of the many concepts that React expanded in the web sphere is the concept of declarative views.

Before declarative views, most of us were modifying the DOM by calling functions that explicitly changed it.

As mentioned at the introduction of this article, we are now declaring views based on a state, and — though we are still calling functions to alter this state — we are not in control of when the DOM will change or even if it should change.

Because of this inversion of control, we’d lose this imperative nature if it weren’t for refs.

Here are a few use cases where it may make sense to bring refs into your code.

Focus control

You can achieve focus in an element programmatically by calling focus() on the node instance.

Because the DOM exposes this as a function call, the best way to do this in React is to create a ref and manually do it when we think it’s suitable.

import React from "react";

class InputModal extends React.Component {
  constructor(props) {
    super(props);

    this.state = { value: props.initialValue };
  }

  onChange = e => {
    this.setState({ value: e.target.value });
  };

  onSubmit = e => {
    e.preventDefault();
    const { value } = this.state;
    const { onSubmit, onClose } = this.props;
    onSubmit(value);
    onClose();
  };
  render() {
    const { value } = this.state;

    return (
      <div className="modal--overlay">
        <div className="modal">
          <h1>Insert a new value</h1>
          <form action="?" onSubmit={this.onSubmit}>
            <input
              type="text"
              onChange={this.onChange}
              value={value}
            />
            <button>Save new value</button>
          </form>
        </div>
      </div>
    );
  }
}

export default InputModal;

In this modal, we allow the user to modify a value already set in the screen below. It would be a better user experience if the input was on focus when the modal opens.

This could enable a smooth keyboard transition between the two screens.

The first thing we need to do is get a reference for the input:

import React, { createRef } from "react";

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

    this.state = { value: props.initialValue };
  }

  onChange = e => {
    this.setState({ value: e.target.value });
  };

  onSubmit = e => {
    e.preventDefault();
    const { value } = this.state;
    const { onSubmit, onClose } = this.props;
    onSubmit(value);
    onClose();
  };

  render() {
    const { value } = this.state;

    return (
      <div className="modal--overlay">
        <div className="modal">
          <h1>Insert a new value</h1>
          <form action="?" onSubmit={this.onSubmit}>
            <input
              ref={this.inputRef}
              type="text"
              onChange={this.onChange}
              value={value}
            />
            <button>Save new value</button>
          </form>
        </div>
      </div>
    );
  }
}

export default InputModal;

Next, when our modal mounts, we imperatively call focus on our input ref:

import React, { createRef } from "react";
    
    class InputModal extends React.Component {
      constructor(props) {
        super(props);
        this.inputRef = createRef();
    
        this.state = { value: props.initialValue };
      }
    
      componentDidMount() {
        this.inputRef.current.focus();
      }
    
      onChange = e => {
        this.setState({ value: e.target.value });
      };
    
      onSubmit = e => {
        e.preventDefault();
        const { value } = this.state;
        const { onSubmit, onClose } = this.props;
        onSubmit(value);
        onClose();
      };
    
      render() {
        const { value } = this.state;
    
        return (
          <div className="modal--overlay">
            <div className="modal">
              <h1>Insert a new value</h1>
              <form action="?" onSubmit={this.onSubmit}>
                <input
                  ref={this.inputRef}
                  type="text"
                  onChange={this.onChange}
                  value={value}
                />
                <button>Save new value</button>
              </form>
            </div>
          </div>
        );
      }
    }
    
    export default InputModal;

Remember that you need to access the element through the current property.

Detect if an element is contained

Similarly, sometimes you want to know if any element dispatching an event should trigger some action on your app. For example, our Modal component could get closed if you click outside of it:

import React, { createRef } from "react";

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

    this.state = { value: props.initialValue };
  }

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

    document.body.addEventListener("click", this.onClickOutside);
  }

  componentWillUnmount() {
    document.removeEventListener("click", this.onClickOutside);
  }

  onClickOutside = e => {
    const { onClose } = this.props;
    const element = e.target;

    if (this.modalRef.current
      && !this.modalRef.current.contains(element)) {
      e.preventDefault();
      e.stopPropagation();
      onClose();
    }
  };

  onChange = e => {
    this.setState({ value: e.target.value });
  };

  onSubmit = e => {
    e.preventDefault();
    const { value } = this.state;
    const { onSubmit, onClose } = this.props;
    onSubmit(value);
    onClose();
  };

  render() {
    const { value } = this.state;
    return (
      <div className="modal--overlay">
        <div className="modal" ref={this.modalRef}>
          <h1>Insert a new value</h1>
          <form action="?" onSubmit={this.onSubmit}>
            <input
              ref={this.inputRef}
              type="text"
              onChange={this.onChange}
              value={value}
            />
            <button>Save new value</button>
          </form>
        </div>
      </div>
    );
  }
}

export default InputModal;

Here, we are checking if the element click is out of the modal limits.

If it is, then we are preventing further actions and calling the onClose callback, since the Modal component expects to be controlled by its parent.

Remember to check if the DOM element current reference still exists as state changes in React are asynchronous.

To achieve this, we are adding a global click listener on the body element. It’s important to remember to clean the listener when the element gets unmounted.

Integrating with DOM-based libraries

As good as React is, there are a lot of utilities and libraries outside its ecosystem that have been in use on the web for years.

It’s good to take advantage of their stability and resolution for some specific problems.

GreenSock library is a popular choice for animation examples. To use it, we need to send a DOM element to any of its methods.

Using refs allows us to combine React with a great animation library.

Let’s go back to our modal and add some animation to make its entrance fancier.

import React, { createRef } from "react";
import gsap from "gsap";

class InputModal extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = createRef();
    this.modalRef = createRef();
    this.overlayRef = createRef();

    this.state = { value: props.initialValue };

    const onComplete = () => {
      this.inputRef.current.focus();
    };
    const timeline = gsap.timeline({ paused: true, onComplete });
    this.timeline = timeline;
  }
  componentDidMount() {
    this.timeline
      .from(this.overlayRef.current, {
        duration: 0.25,
        autoAlpha: 0
      })
      .from(this.modalRef.current, {
        duration: 0.25,
        autoAlpha: 0,
        y: 25
      });
    this.timeline.play();

    document.body.addEventListener("click", this.onClickOutside);
  }
  componentWillUnmount() {
    this.timeline.kill();
    document.removeEventListener("click", this.onClickOutside);
  }

  onClickOutside = e => {
    const { onClose } = this.props;
    const element = e.target;
    if (this.modalRef.current
        && !this.modalRef.current.contains(element)) {
      e.preventDefault();
      e.stopPropagation();
      onClose();
    }
  };

  onChange = e => {
    this.setState({ value: e.target.value });
  };

  onSubmit = e => {
    e.preventDefault();
    const { value } = this.state;
    const { onSubmit, onClose } = this.props;
    onSubmit(value);
    onClose();
  };

  render() {
    const { value } = this.state;
    return (
      <div className="modal--overlay" ref={this.overlayRef}>
        <div className="modal" ref={this.modalRef}>
          <h1>Insert a new value</h1>
          <form action="?" onSubmit={this.onSubmit}>
            <input
              ref={this.inputRef}
              type="text"
              onChange={this.onChange}
              value={value}
            />
            <button>Save new value</button>
          </form>
        </div>
      </div>
    );
  }
}

export default InputModal;

At the constructor level, we are setting up the initial animation values, which will modify the styles of our DOM references. The timeline only plays when the component mounts.

When the element gets unmounted, we’ll clean the DOM state and actions by terminating any ongoing animation with the kill() method supplied by the Timeline instance.

We’ll turn our focus to the input after the timeline has completed.

Rule of thumb for refs usage

After knowing how refs work, it’s easy to use them where they’re not needed.

There’s more than one way to achieve the same thing inside a React component, so it’s easy to fall into an anti-pattern.

My rule when it comes to ref usage is this: You need to imperatively call a function for a behavior React doesn’t allow you to control.

A simpler way to put it would be this: You need to call a function, and that function has no association with a React method or artifact.

Let’s explore an anti-pattern that I’ve seen repeatedly in articles and even in interviews.

import React, { createRef } from 'react';

class Form extends React.Component {
  constructor(props) {
    super(props)
    this.inputRef = createRef()
  
    this.state = { storedValue: '' }
  }

  onSubmit => (e) {
    e.preventDefault()
    this.setState({ storedValue: this.inputRef.current.value })
  }  

  render() {

    return (
      <div className="modal">
        <form action="?" onSubmit={this.onSubmit}>
          <input
            ref={this.inputRef}
            type="text"
          />
          <button>Submit</button>
        </form>
      </div>
    )
  }  
}

It’s fair to say that if you want to send a value on submit, this approach will work.

The issue is that, knowing refs are actually an escape hatch of the view model React offers, we are sniffing into DOM element values or properties that we have access to through the React interface.

Controlling the input value we can always check its value.

render() {
  const { value } = this.state

  return (
    <input
      type="text"
      onChange={e => this.setState({ value: e.target.value })}
      value={value}
    />
  )
}

Let’s go back to our rule: “You need to imperatively call a function for a behavior React doesn’t allow you to control. ”

In our uncontrolled input we are creating a ref but not doing an imperative call. Then that function should exist, which is not satisfied as I can indeed control an input’s value.

Forwarding refs

As we’ve discussed, refs are actually useful for really specific actions. The examples shown are a little simpler than what we usually find in a web application codebase nowadays.

Components are more complex and we barely use plain HTML elements directly. It’s really common to include more than one node to encapsulate more logic around the view behavior.

import React from 'react'

const LabelledInput = (props) => {
  const { id, label, value, onChange } = props

  return (
    <div class="labelled--input">
      <label for={id}>{label}</label>
      <input id={id} onChange={onChange} value={value} />
    </div>
  )
}

export default LabelledInput

The issue now is that passing a ref to this component will return its instance, a React component reference, and not the input element we want to focus on like in our first example.

Luckily, React provides an out-of-the-box solution for this called forwardRef, which allows you to define internally what element the ref will point at.

import React from 'react'

const LabelledInput = (props, ref) => {
  const { id, label, value, onChange } = props

  return (
    <div class="labelled--input">
      <label for={id}>{label}</label>
      <input id={id} onChange={onChange} value={value} ref={ref}/>
    </div>
  )
}

export default React.forwardRef(InputCombo)

To achieve this, we’ll pass a second argument to our function and place it in the desired element.

Now, when a parent component passes a ref value, it’s going to obtain the input, which is helpful to avoid exposing internals and properties of a component and breaking its encapsulation.

The example of our form that we saw failing at achieving focus will now work as expected.

Conclusion

We started with a recap on the basic concepts of React and its usage, why we generally shouldn’t break the framework’s model, and why we may sometimes need to.

Accessing the DOM through the interface the library exposes helps to maintain the internals of React in place (remember that setState contains more logic than just triggering a re-render cycle, like batching updates and in the near future, time slicing).

Breaking this model with anti-patterns can make later performance improvements in the library useless or even create bugs in your applications.

Remember to use refs only when there is an implicit function call React can’t handle through its methods.

Also, make sure it doesn’t alter the internal state of the components.

For more information, read the official React documentation about refs.

Hire React Js Developer from Expert React JS Development Company

Hire React Js Developer from Expert React JS Development Company

Are you looking to [hire React JS developer](https://www.mobiwebtech.com/react-js-development-company/ "hire React JS developer") from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team...

Are you looking to hire React JS developer from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team of expert React JS developers, you can hire them on an hourly, weekly or monthly basis for the successful completion of your dream project.

Learn React.js: Front-End Web Development for Beginners

Learn React.js: Front-End Web Development for Beginners

Learn React.js - Frontend Web Development for Beginners. In this React tutorial, you will learn the core fundamentals of React JS and Modern JavaScript so that you can start building lightning fast web apps using React JS. Learn Modern JavaScript and React JS from absolute scratch. Learn to make AJAX requests to get data from remote API and display into your web application. Learn React JS, which is one of the most exciting technology of recent time

Learn React.js - Frontend Web Development for Beginners - Web Development in 2019. In this video you will learn the core fundamentals of React JS and Modern JavaScript so that you can start building lightning fast web apps using React JS.

In this video, you will learn Modern JavaScript and React JS from absolute scratch. you will learn to make AJAX requests to get data from remote API and display into your web application.

So if you are ready to learn React JS, which is one of the most exciting technology of recent time

Thanks for watching

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

React - The Complete Guide (incl Hooks, React Router, Redux)

**☞ **Modern React with Redux [2019 Update]

**☞ **50+ Java Interview Questions for Programmers

**☞ **Top 100 Python Interview Questions and Answers

**☞ **100+ Basic Machine Learning Interview Questions and Answers

**☞ **Introduction to Java String Interview Questions and Answers

**☞ **Best 50 React Interview Questions and Answers in 2019

**☞ **Top 50+ SQL Interview Questions and Answers in 2019

**☞ **Best Java Microservices Interview Questions In 2019

**☞ **Best 50 Nodejs interview questions from Beginners to Advanced in 2019

**☞ **100+ Java Interview Questions and Answers In 2019

Top Vue.js Developers in USA

Top Vue.js Developers in USA

Vue.js is an extensively popular JavaScript framework with which you can create powerful as well as interactive interfaces. Vue.js is the best framework when it comes to building a single web and mobile apps.

We, at HireFullStackDeveloperIndia, implement the right strategic approach to offer a wide variety through customized Vue.js development services to suit your requirements at most competitive prices.

Vue.js is an open-source JavaScript framework that is incredibly progressive and adoptive and majorly used to build a breathtaking user interface. Vue.js is efficient to create advanced web page applications.

Vue.js gets its strength from the flexible JavaScript library to build an enthralling user interface. As the core of Vue.js is concentrated which provides a variety of interactive components for the web and gives real-time implementation. It gives freedom to developers by giving fluidity and eases the integration process with existing projects and other libraries that enables to structure of a highly customizable application.

Vue.js is a scalable framework with a robust in-build stack that can extend itself to operate apps of any proportion. Moreover, vue.js is the best framework to seamlessly create astonishing single-page applications.

Our Vue.js developers have gained tremendous expertise by delivering services to clients worldwide over multiple industries in the area of front-end development. Our adept developers are experts in Vue development and can provide the best value-added user interfaces and web apps.

We assure our clients to have a prime user interface that reaches end-users and target the audience with the exceptional user experience across a variety of devices and platforms. Our expert team of developers serves your business to move ahead on the path of success, where your enterprise can have an advantage over others.

Here are some key benefits that you can avail when you decide to hire vue.js developers in USA from HireFullStackDeveloperIndia:

  • A team of Vue.js developers of your choice
  • 100% guaranteed client satisfaction
  • Integrity and Transparency
  • Free no-obligation quote
  • Portal development solutions
  • Interactive Dashboards over a wide array of devices
  • Vue.js music and video streaming apps
  • Flexible engagement model
  • A free project manager with your team
  • 24*7 communication with your preferred means

If you are looking to hire React Native developers in USA, then choosing HireFullStackDeveloperIndia would be the best as we offer some of the best talents when it comes to Vue.js.