DEV Community

Cover image for React.js ~Tips for making UI that is reusable and testable~
Ogasawara Kakeru
Ogasawara Kakeru

Posted on

React.js ~Tips for making UI that is reusable and testable~

Compound Component Pattern

If you find yourself passing props through multiple layers of components, consider using the Compound Component Pattern.

The Compound Component Pattern is a powerful design pattern that is widely used in React applications. It can improve reusability, readability, and scalability.

This pattern builds a UI by combining multiple related child components under a shared parent component.

A familiar example is the element and its elements. These elements are designed to work together rather than independently. The element manages the selected value, while the elements represent the available choices.

Similarly, in the Compound Component Pattern, state is typically managed by the parent component and shared with its child components.

This pattern also establishes an implicit relationship between the parent and child components while still allowing consumers to customize the component structure.

Without the Compound Component Pattern

First, let’s implement a counter component without using the Compound Component Pattern.

You might notice that the component starts to become a little complicated because it has to manage the counter state, conditionally apply styles based on that state, and define the behavior of each UI element.

Counter.tsx

const Counter = () => {
  const { count, handleDecrementClick, handleIncrementClick } = useCounter();
  return (
    <>
      <p className="count">
        Count: <span className={count <= 10 ? "red" : ""}>{count}</span>
      </p>
      <button className="minus-button" onClick={handleDecrementClick}>
        -
      </button>
      <button className="plus-button" onClick={handleIncrementClick}>
        +
      </button>
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

Using the Compound Component Pattern

Now, let’s implement the same component using the Compound Component Pattern.

You might notice that the component becomes much more declarative and easier to read.

Counter.tsx

const CounterExample = () => {
  const handleChangeCounter = (count: number) => {
    console.log("count", count);
  };
  return (
    <Counter onChange={handleChangeCounter}>
      <Counter.Label>
        Count: <Counter.Count max={10} />
      </Counter.Label>
      <Counter.Increment>+</Counter.Increment>
      <Counter.Decrement>-</Counter.Decrement>
    </Counter>
  );
};
Enter fullscreen mode Exit fullscreen mode

With the Compound Component Pattern, each child component has a clear responsibility.

The parent component manages the shared state, while components such as , , and provide specific pieces of functionality.

As a result, users of the component can understand its structure directly from the JSX and can arrange the child components more flexibly without having to manage the internal state themselves.

Top comments (0)