Blog Details
RUHUL AMIN PARVEZ
15 Oct 2024
5 min read
In React development, understanding advanced component composition patterns can elevate your ability to build flexible, reusable, and scalable applications. This blog will cover three powerful composition patterns: Compound Components, Render Props, and Higher-Order Components (HOCs).
By the end, you’ll know when and how to use these patterns to solve complex UI challenges in a maintainable and elegant way.
Compound components allow related components to share state and behavior in a flexible, decoupled way. These components work together like a team, allowing for a more declarative API, perfect for building reusable libraries like dropdowns, tabs, or modals.
Let's build a simple Accordion using compound components. The parent component will manage the state and share it with the children.
import React, { useState, createContext, useContext } from 'react';
const AccordionContext = createContext();
const Accordion = ({ children }) => {
const [openIndex, setOpenIndex] = useState(null);
const toggleIndex = (index) => {
setOpenIndex(index === openIndex ? null : index);
};
return (
<AccordionContext.Provider value={{ openIndex, toggleIndex }}>
<div className="accordion">{children}</div>
</AccordionContext.Provider>
);
};
The AccordionItem
, AccordionHeader
, and AccordionBody
components will consume the context to know when to expand or collapse.
const AccordionItem = ({ index, children }) => {
const { openIndex } = useContext(AccordionContext);
return <div>{openIndex === index && children}</div>;
};
const AccordionHeader = ({ index, children }) => {
const { toggleIndex } = useContext(AccordionContext);
return <h2 onClick={() => toggleIndex(index)}>{children}</h2>;
};
const AccordionBody = ({ children }) => {
return <div>{children}</div>;
};
Now, use the compound components with a clean and intuitive API.
const App = () => {
return (
<Accordion>
<AccordionHeader index={0}>Section 1</AccordionHeader>
<AccordionItem index={0}>
<AccordionBody>Content for section 1</AccordionBody>
</AccordionItem>
<AccordionHeader index={1}>Section 2</AccordionHeader>
<AccordionItem index={1}>
<AccordionBody>Content for section 2</AccordionBody>
</AccordionItem>
</Accordion>
);
};
Render Props is a pattern where a function is passed as a prop to a component, allowing it to dynamically render based on internal state or behavior. It’s useful when different components need to share state or behavior but need different UIs.
Let's build a MouseTracker
component that tracks the mouse position and shares the state with a child component via a render prop.
import React, { useState } from 'react';
const MouseTracker = ({ render }) => {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (e) => {
setMousePosition({ x: e.clientX, y: e.clientY });
};
return <div onMouseMove={handleMouseMove}>{render(mousePosition)}</div>;
};
Pass a function to the render
prop to control what’s displayed when the mouse moves.
const App = () => {
return (
<div>
<h1>Move your mouse around!</h1>
<MouseTracker
render={({ x, y }) => (
<p>
The mouse position is ({x}, {y})
</p>
)}
/>
</div>
);
};
Higher-Order Components (HOCs) are functions that take a component and return a new component with enhanced behavior or additional props. This pattern is great for code reuse, particularly for things like authentication, logging, or theming.
Let’s build a simple HOC called withLogger
that logs when a component mounts.
import React, { useEffect } from 'react';
const withLogger = (WrappedComponent) => {
return (props) => {
useEffect(() => {
console.log(`Component ${WrappedComponent.name} mounted.`);
}, []);
return <WrappedComponent {...props} />;
};
};
Wrap any component you want with withLogger
to get the logging functionality.
const MyComponent = () => {
return <div>Hello, I’m a component!</div>;
};
const MyComponentWithLogger = withLogger(MyComponent);
const App = () => {
return <MyComponentWithLogger />;
};
Understanding and applying these advanced component composition patterns—Compound Components, Render Props, and Higher-Order Components (HOCs)—can make your React applications much more flexible and maintainable.
Mastering these patterns will enable you to write React components that are both easy to use and highly reusable across different parts of your application.
Don’t worry, we don’t spam!