eMoosavi
Weekly frontend dev braindumps
Demystifying React Refs and DOM Manipulation: Advanced Patterns for Complex UIs
React Components

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:104 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:

  1. Avoid Overuse: Don't use Refs to solve problems that can be solved with state or props.
  2. Cleanup: Make sure to clean up Refs when a component unmounts to avoid memory leaks.
  3. 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.

Resources

Article tags
reactrefsdom-manipulationreact-componentsweb-development
Previous article

Frontend Architecture

Rethinking CSS-in-JS: Advanced Techniques with Styled Components in React

Next article

React Components

Crafting Polymorphic Components in React