Enhancing Your React Skills with Refs

Savan Chhayani
TechVerito
Published in
5 min readJul 26, 2024
Photo by Afif Ramdhasuma on Unsplash

React refs are a powerful feature that allows you to directly interact with DOM elements and React components. This blog will explore why and when to use refs, the different ways to use them, how to access functional component methods using refs, and additional tips for using refs effectively.

My Journey with React Refs

When I first started working with React, I was fascinated by its declarative approach and the way it manages the DOM for you. However, I quickly realized that there are situations where you need more control over DOM elements. This is where I discovered the power of React refs.

Why and When to use Refs

Refs are used in React to:

  1. Access DOM elements Directly: Sometimes you need to interact directly with a DOM element, such as focusing an input field, playing or pausing a video, or measuring the size of an element.
  2. Store Mutable Values: Refs can be used to hold mutable values that do not trigger a re-render when changed.
  3. Access Child Component Methods: Refs can be used to call methods or access properties of child components.

You should use refs sparingly as they can make the code harder to manage and debug. Preferably, you should use controlled components and state management solutions for most use cases. However, refs are beneficial when you need to bypass React’s declarative approach and work directly with the DOM.

Different Ways to Use Refs

There are several ways to create and use refs in React:

  1. Create Refs using React.createRef() :
import React, { Component } from 'react';

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

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

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

export default Input;

2. Using the useRef Hook in Functional Components:

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

const Input = () => {
const inputRef = useRef(null);

useEffect(() => {
inputRef.current.focus();
}, []);

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

export default Input;

3. Callback Refs:

import React, { useEffect } from 'react';

const Input = () => {
let inputRef = null;

const setInputRef = (element) => {
inputRef = element;
};

useEffect(() => {
if (inputRef) {
inputRef.focus();
}
}, [inputRef]);

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

export default Input;

Accessing Functional Component Methods Using Refs

With the introduction of the React hooks, we can use useImperativeHandle along with forwardRef to expose methods from a functional component to parent components via refs.

Here’s an example demonstrating how to achieve this:

  1. Creating a Functional Component with Exposed Methods:
import React, { useRef, useImperativeHandle, forwardRef } from 'react';

const Input = forwardRef((props, ref) => {
const inputRef = useRef();

useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = '';
}
}));

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

export default Input;

2. Using the Child(Input) Component’s Methods in a Parent Component:

import React, { useRef } from 'react';
import Input from './Input';

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

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

export default ParentComponent;

Refs for Performance Optimization

Refs can be used for performance optimization by avoiding unnecessary re-renders. For example, you can store mutable values in refs instead of state when changes to those values don’t need to trigger re-render.

Real-Life Example: Chat Application
Imagine you have a chat application where new messages are added to the end of a list. When a new message arrives, you want to scroll to the bottom of the chat list to show the latest message. Using state for the scroll position would cause the entire component to re-render, which is unnecessary and can affect performance. Instead, you can use refs to handle this effectively.

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

const Chat = ({ messages }) => {
const messagesEndRef = useRef(null);

const scrollToBottom = () => {
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
};

useEffect(() => {
scrollToBottom();
}, [messages]);

return (
<div style={{ height: '400px', overflowY: 'auto' }}>
<ul>
{messages.map((message, index) => (
<li key={index}>{message}</li>
))}
<div ref={messagesEndRef} />
</ul>
</div>
);
};

export default Chat;

Best Practices for Using Refs

  1. Use Refs Sparingly: Rely on state and props for most data flows in your application.
  2. Avoid Manipulating the DOM Directly: Try to keep your React components declarative. Use refs only when necessary.
  3. Do Not Overuse useImperativeHanlde : Exposing too many methods from child components can lead to tight coupling and make your components harder to maintain.
  4. Clean up Refs: Make sure to clean up refs when no longer needed to avoid memory leaks.

Improvements in React 19: Ref as a Prop

React 19 introduces significant enhancements in handling refs, particularly with functional components. One of the key improvements is the ability to access refs directly as props, eliminating the need for the forwardRef pattern that was previously required.

Ref as a Prop in Functional Components:

function Input({ placeholder, ref }) {
return <input placeholder={placeholder} ref={ref} />;
}

// Usage
import React, { useRef } from 'react';

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

return <Input ref={inputRef} placeholder="Enter your text here" />;
}

export default App;

React 19 in Release Candidate (RC) Phase: As of now, React 19 is in the RC phase. This means the new features, including the ref as a prop, are available for testing and feedback but may still undergo some changes before the final release. It’s a great time to explore and get familiar with these improvements, but you can continue using existing patterns without any issues in production environments.

Conclusion

Refs in React allow direct interaction with the DOM and access to child component methods, making them essential for certain scenarios. They should be used sparingly to escape React’s declarative paradigm. Whether using class or functional components, mastering refs can help you build more interactive and responsive applications. By using refs judiciously and following best practices, you can maintain clean and maintainable code.

--

--

Savan Chhayani
TechVerito

Senior Consultant At Techverito React | JavaScript | TypeScript | Kotlin | Spring Boot | Docker