Demystifying React Refs and DOM Manipulation: Advanced Patterns for Complex UIs
Unlocking the full potential of React Refs for effective and streamlined DOM manipulation
Jun 30, 2024 - 16:10 • 4 min read
React Refs: Not Just About Getting DOM Nodes
React has revolutionized the way we think about front-end development, enabling us to create highly dynamic and testable components. While the declarative nature of React sets it apart, sometimes interacting directly with the DOM is unavoidable. This often leads to a concept that's confusing for many - React Refs. In this post, we'll dive deep into some advanced use-cases and patterns for React Refs that can enhance your productivity and code quality.
What are React Refs?
The basic documentation tells us that Refs can be used to get a reference to a DOM element created in the render method. However, Refs are far more powerful, enabling advanced DOM manipulations, component instance management, and even complex animations.
import React, { useRef, useEffect } from 'react';
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// Accessing the native DOM element through ref
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type='text' />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
Handling Multiple References: The useImperativeHandle Hook
useImperativeHandle
customizes the instance value that is exposed when using ref
in parent components. This can be particularly useful for building reusable component libraries.
import React, { useRef, forwardRef, useImperativeHandle } from 'react';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = '';
}
}));
return <input ref={inputRef} type='text' />;
});
function Parent() {
const inputRef = useRef();
return (
<>
<FancyInput ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus Input</button>
<button onClick={() => inputRef.current.clear()}>Clear Input</button>
</>
);
}
Coordinating Multiple Elements: A Practical Example
In advanced UI scenarios, you might need to coordinate actions across multiple HTML elements. Refs provide a straightforward way to achieve this.
import React, { useRef } from 'react';
function Carousel() {
const imageRefs = useRef([]);
const scrollToImage = (index) => {
if(imageRefs.current[index]) {
imageRefs.current[index].scrollIntoView({ behavior: 'smooth' });
}
};
return (
<div>
<button onClick={() => scrollToImage(0)}>First</button>
<button onClick={() => scrollToImage(1)}>Second</button>
<button onClick={() => scrollToImage(2)}>Third</button>
<div className='carousel'>
{[1, 2, 3].map((val, i) => (
<img key={val} ref={(el) => (imageRefs.current[i] = el)} src={`/images/${val}.jpg`} alt={`Slide ${val}`} />
))}
</div>
</div>
);
}
Advanced Animation Scenarios: Coordinating Refs with React-Spring
When it comes to animations, Refs, in combination with libraries like React-Spring, can unlock powerful effects.
import React, { useRef } from 'react';
import { useSpring, animated } from 'react-spring';
function AnimatedCard() {
const cardRef = useRef(null);
const props = useSpring({
from: { opacity: 0, transform: 'scale(0.8)' },
to: { opacity: 1, transform: 'scale(1)' },
config: {
duration: 1000,
easing: (t) => t * t
}
});
useEffect(() => {
if (cardRef.current) {
cardRef.current.style.backgroundColor = '#f0f0f0';
}
}, []);
return <animated.div ref={cardRef} style={props}>Hello, I am an animated card!</animated.div>;
}
Best Practices and Pitfalls
Using Refs comes with its own set of best practices and potential pitfalls:
- Avoid Overuse: Don't use Refs to solve problems that can be solved with state or props.
- Cleanup: Make sure to clean up Refs when a component unmounts to avoid memory leaks.
- Avoid Direct DOM Manipulations: Interacting directly with the DOM can jeopardize React's declarative nature. Use sparingly and only when necessary.
Conclusion
Refs are a complex yet powerful feature of React that can provide you with much-needed direct access to DOM nodes and component instances. Whether you're managing form controls, creating complex animations, or coordinating actions between multiple elements, Refs serve as an essential tool. But, like any powerful tool, they should be used carefully and judiciously.