Clean React code pattern with the useImperativeHandle hook



This content originally appeared on DEV Community and was authored by Tony Edgal

useImperativeHandle hook patternIn the quest for a cleaner and more concise codebase that adheres to the DRY principle, one React hook that stands out is the useImperativeHandle hook.

React’s declarative nature encourages developers to build components that rely on props and state to manage behavior and rendering. However, in some cases, imperative programming can simplify complex state management, especially when a child component interacts with multiple parents.

The useImperativeHandle() hook Pattern

In React, as the app size grows, it also starts to grow in complexity and it becomes expedient for devs to utilize advanced patterns to reach their goals, as simpler patterns can no longer adequately provide the clean solutions we require.

Managing state in child components typically involves lifting state up to parent components. However, when a child is shared among multiple parents, duplicating state can lead to repetitive code and potential inconsistencies. To address this, you can centralize state management within the child itself, using the useImperativeHandle hook to expose imperative methods for modifying its state. This approach not only prevents redundancy but also promotes a more maintainable architecture.

The Pattern in Action

Below is an example that demonstrates how to leverage useImperativeHandle for better state management. In this pattern, a child component exposes methods like focus and clear through its ref, allowing parent components to control its behavior without relying solely on props or state hooks.

Example
import React, { useRef, forwardRef, useImperativeHandle } from 'react';

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

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

  return <input ref={inputRef} {...props} />;
});

export default function ParentComponent() {
  const inputRef = useRef();

  return (
    <div className="p-4 space-y-4">
      <ChildInput ref={inputRef} placeholder="Type something..." />
      <div className="space-x-2">
        <button
          onClick={() => inputRef.current.focus()}
          className="px-3 py-1 bg-blue-500 text-white rounded"
        >
          Focus Input
        </button>
        <button
          onClick={() => inputRef.current.clear()}
          className="px-3 py-1 bg-red-500 text-white rounded"
        >
          Clear Input
        </button>
      </div>
    </div>
  );
}

React 19 further refines this pattern by eliminating the need for forwardRef. The example below illustrates the same behavior with a cleaner syntax.

React 19 Example
import React, { useImperativeHandle, useRef } from 'react';

function ChildInput(props, ref) {
  const inputRef = useRef();

  // Expose imperative methods to the parent via the ref.
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current?.focus(),
    clear: () => {
      if (inputRef.current) {
        inputRef.current.value = '';
      }
    },
  }));

  return <input ref={inputRef} {...props} />;
}

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

  return (
    <div className="p-4 space-y-4">
      <ChildInput ref={inputRef} placeholder="Type something..." />
      <div className="space-x-2">
        <button
          onClick={() => inputRef.current.focus()}
          className="px-3 py-1 bg-blue-500 text-white rounded"
        >
          Focus Input
        </button>
        <button
          onClick={() => inputRef.current.clear()}
          className="px-3 py-1 bg-red-500 text-white rounded"
        >
          Clear Input
        </button>
      </div>
    </div>
  );
}

export default ParentComponent;

Conclusion

This approach demonstrates how useImperativeHandle can simplify state management in complex React applications. While the example above provides a practical solution, I would encourage you to learn more about useImperativeHandle, Declarative and Imperative programming,

I have provided starter prompts you can feed into an LLM to get detailed explanations for each of the concepts, and to fully appreciate the trade-offs and best practices behind this each of them.

Explain declarative and imperative programming?
Examples of declarative and imperative programming in React?
Explain the useImperativeHandle hook, and give examples of when to this hook should be used?


This content originally appeared on DEV Community and was authored by Tony Edgal