What are forward refs in React.js?

What are forward refs in React.js?

In the ever-evolving landscape of React.js, understanding the nuances of certain concepts can set your application apart. One of those nuanced concepts is “forward refs”. To provide an impeccable user experience and to ensure that React components communicate flawlessly, we sometimes need to dig into some advanced React features. Forward refs are one of those features that allow React components to better interact with the DOM and other components.

Basics of Refs in React

Refs are a crucial part of React which offer a way to access the DOM nodes or React elements created in the render method.

What are refs?

At its core, refs are about getting a reference to a DOM node or an instance of a component in React. Here’s how it typically works:

  • The createRef() method: Introduced in React 16.3, this method creates a ref object. When this ref is attached to a React element in the render method, we can use the ref to get the actual DOM node.
    class MyComponent extends React.Component {
    constructor(props) {
    super(props);
    this.myRef = React.createRef();
    }

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

  • Accessing DOM elements and instance methods using refs: Once a ref is attached to an element, it can be used to access the element’s DOM node directly. This allows you to do things that wouldn’t be possible with just React.
    componentDidMount() {
    this.myRef.current.focus();
    }

Common use cases for refs

Refs aren’t something you’d use in every component but they are exceptionally handy in specific scenarios:

  • Focusing input elements: Autofocus on a text input when a modal opens.
  • Handling animations: Directly accessing DOM nodes can be useful when working with animation libraries.
  • Integrating with third-party DOM libraries: Sometimes, you might need to wrap around a third-party library which directly manipulates the DOM.

Limitations of Normal Refs

As useful as refs might be, traditional refs do come with limitations.

Passing refs to function components

By default, you cannot use refs on function components because they don’t have instances. This was a challenge because the community was shifting towards more functional components with hooks.

Issues with higher-order components

Higher-order components (HOCs) are functions that wrap around components and return a new component. The problem arises when we try to pass a ref to this wrapped component. The ref gets attached to the HOC, not the wrapped component, leading to potential issues and confusion.

Introducing Forward Refs

To overcome these limitations, React introduced “forward refs”. This mechanism allows a component to forward the ref it receives to another component or DOM element.

How Forward Refs Work

Essentially, forward refs allow a component to expose a ref passed to it, to one of its child components. This is especially useful when you want to interact with the child’s DOM element directly or call one of its instance methods.

The React.forwardRef API

React provides a forwardRef method to achieve this:

1const ButtonWithRef = React.forwardRef((props, ref) => (
2 <button ref={ref} {...props}>
3 Click me!
4 </button>
5));
6
7// Usage
8const App = () => {
9 const buttonRef = React.createRef();
10
11 return <ButtonWithRef ref={buttonRef} />;
12};

Here, the ButtonWithRef component forwards the ref it receives to the button element inside it.

Using Forward Refs

Let’s take a closer look at the practical application of forward refs.

Basic Syntax and Usage

Using forward refs is fairly straightforward. Here’s a simple example:

1// A functional component that forwards a ref
2const TextInput = React.forwardRef((props, ref) => (
3 <input type="text" ref={ref} {...props} />
4));
5
6// Using the TextInput component
7function App() {
8 const inputRef = React.createRef();
9
10 function focusTextInput() {
11 inputRef.current.focus();
12 }
13
14 return (
15 <div>
16 <TextInput ref={inputRef} />
17 <button onClick={focusTextInput}>Focus the input</button>
18 </div>
19 );
20}

In the above example, the TextInput component forwards its ref to the underlying input element. This allows the parent App component to directly interact with the DOM input element.

Accessing Refs in Forwarded Components

In React, forwarding refs allow a component to expose its reference to a child component. When you wrap a component with React.forwardRef, you can access its ref from a parent component. Here’s a basic example:

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

// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

In this example, the FancyButton component forwards its ref to a DOM button. This means that when we use the FancyButton component, we can actually get a reference to the inner DOM button.

Real-world Use Cases of Forward Refs

Forward refs are particularly useful in certain scenarios. Here are some places where they shine:

Component Libraries and Styled-components

When building component libraries, it’s essential to allow users of the library to access the underlying DOM node of a component. For instance, in styled-components, a popular styling library for React, components are often styled wrappers around native DOM elements. Forward refs ensure that users can still access the underlying DOM node.

const StyledButton = styled.button`
/* Some styles here */
`;

const MyButton = React.forwardRef((props, ref) => (
<StyledButton ref={ref} {...props} />
));

Wrapping Components

In Higher Order Components (HOCs) or when wrapping components, maintaining ref integrity becomes crucial. If a wrapped component exposes a ref and you wrap it with another component, the outer component should forward the ref to the inner one. Otherwise, you break ref access for the parent.

Forward Ref with useImperativeHandle

useImperativeHandle is a hook in React that works hand-in-hand with forwardRef to customize the instance value exposed when using ref.

Customizing Instance Values

Consider a scenario where you want to expose specific methods of a component, and not the whole component instance. This is where useImperativeHandle shines:

1const FancyInput = React.forwardRef((props, ref) => {
2 const inputRef = useRef();
3
4 useImperativeHandle(ref, () => ({
5 focus: () => {
6 inputRef.current.focus();
7 },
8 }));
9
10 return <input ref={inputRef} {...props} />;
11});

Here, when you get a ref to FancyInput, it will only have a .focus() method.

Potential Pitfalls and Best Practices

Overusing Forward Refs

Just because you can use forward refs doesn’t mean you always should. They’re a tool for specific scenarios and overusing them can make code harder to maintain and understand.

Compatibility and Performance

Forward refs were introduced in React 16.3. So, if you’re building a library and need to support older versions of React, be mindful. Also, while the performance impact is negligible in most cases, unnecessary use can add overhead.

Tips and Tricks

Merging Multiple Refs

In situations where you need to merge refs, libraries like merge-refs can be handy:

import mergeRefs from "merge-refs";

function MyComponent(props) {
const internalRef = useRef();
return <div ref={mergeRefs([internalRef, props.forwardedRef])} />;
}

Debugging Forwarded Refs

For debugging, make use of React DevTools. If a component is wrapped with forwardRef, it will be shown with a distinct icon in the hierarchy.

Conclusion

Forward refs in React provide an elegant mechanism to access underlying DOM nodes or child component instances. While they solve certain challenges, it’s essential to use them judiciously and understand the associated trade-offs.

Further Reading & Resources

Sharing is caring

Did you like what Mayank Sharma wrote? Thank them for their work by sharing it on social media.

0/10000

No comments so far