Skip to content
intermediatePhase 36 · React

Component Composition

Design flexible UIs with composition patterns, render props, and children.

45m
0 problems
Topic Progress0%

Composition Patterns

Composition Patterns

React favors composition over inheritance.

Basic Composition

function App() {
  return (
    <Layout>
      <Header />
      <Sidebar />
      <Content />
      <Footer />
    </Layout>
  );
}

function Layout({ children }) {
  return (
    <div className="layout">
      <header>Logo</header>
      <main>{children}</main>
      <footer>© 2024</footer>
    </div>
  );
}

Slot Pattern

function Card({ header, footer, children }) {
  return (
    <div className="card">
      {header && <div className="card-header">{header}</div>}
      <div className="card-body">{children}</div>
      {footer && <div className="card-footer">{footer}</div>}
    </div>
  );
}

// Usage
<Card
  header={<h2>Title</h2>}
  footer={<button>Save</button>}
>
  <p>Card content</p>
</Card>

Higher-Order Components (HOC)

// HOC that adds logging
function withLogging(WrappedComponent) {
  return function WithLogging(props) {
    useEffect(() => {
      console.log(`${WrappedComponent.name} rendered`);
    });

    return <WrappedComponent {...props} />;
  };
}

// Usage
const LoggedButton = withLogging(Button);

Compound Components

function Tabs({ children }) {
  const [activeTab, setActiveTab] = useState(0);

  return (
    <div className="tabs">
      {React.Children.map(children, (child, index) =>
        React.cloneElement(child, {
          isActive: index === activeTab,
          onClick: () => setActiveTab(index)
        })
      )}
    </div>
  );
}

function Tab({ children, isActive, onClick }) {
  return (
    <button className={isActive ? "active" : ""} onClick={onClick}>
      {children}
    </button>
  );
}

// Usage
<Tabs>
  <Tab>Tab 1</Tab>
  <Tab>Tab 2</Tab>
  <Tab>Tab 3</Tab>
</Tabs>

Render Props

Render Props

A render prop is a function prop that returns JSX.

Basic Render Prop

function MouseTracker({ render }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMouseMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY });
    };

    window.addEventListener("mousemove", handleMouseMove);
    return () => window.removeEventListener("mousemove", handleMouseMove);
  }, []);

  return render(position);
}

// Usage
function App() {
  return (
    <MouseTracker
      render={({ x, y }) => (
        <p>Mouse is at ({x}, {y})</p>
      )}
    />
  );
}

Render Prop with Data

function DataFetcher({ url, render }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [url]);

  return render({ data, loading, error });
}

// Usage
<DataFetcher
  url="/api/users"
  render={({ data, loading, error }) => {
    if (loading) return <Spinner />;
    if (error) return <Error message={error} />;
    return <UserList users={data} />;
  }}
/>

Render Props vs Hooks

// Render Prop pattern (older)
function useMousePosition() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMouseMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY });
    };

    window.addEventListener("mousemove", handleMouseMove);
    return () => window.removeEventListener("mousemove", handleMouseMove);
  }, []);

  return position;
}

// Custom Hook (modern)
function MouseTracker() {
  const position = useMousePosition();
  return <p>Mouse at ({position.x}, {position.y})</p>;
}

Children as Function

function Toggle({ children }) {
  const [on, setOn] = useState(false);

  return children({
    on,
    toggle: () => setOn((prev) => !prev)
  });
}

// Usage
<Toggle>
  {({ on, toggle }) => (
    <div>
      <p>Toggle is {on ? "ON" : "OFF"}</p>
      <button onClick={toggle}>Toggle</button>
    </div>
  )}
</Toggle>

Children Prop

Children Prop

Basic Children

function Card({ children }) {
  return <div className="card">{children}</div>;
}

// Usage
<Card>
  <h2>Title</h2>
  <p>Content</p>
</Card>

Children with Additional Props

function Alert({ children, type = "info" }) {
  return <div className={`alert alert-${type}`}>{children}</div>;
}

// Usage
<Alert type="error">Something went wrong!</Alert>

Multiple Children Slots

function Modal({ header, footer, children }) {
  return (
    <div className="modal">
      <div className="modal-header">{header}</div>
      <div className="modal-body">{children}</div>
      <div className="modal-footer">{footer}</div>
    </div>
  );
}

// Usage
<Modal
  header={<h2>Confirm</h2>}
  footer={<button>OK</button>}
>
  <p>Are you sure?</p>
</Modal>

React.Children Utilities

function List({ children }) {
  // Count children
  const count = React.Children.count(children);

  // Map over children
  const items = React.Children.map(children, (child, index) => {
    return <li key={index}>{child}</li>;
  });

  // Check if children exist
  if (!children) return null;

  return (
    <div>
      <p>Items: {count}</p>
      <ul>{items}</ul>
    </div>
  );
}

Cloning Children

function Parent({ children }) {
  // Add props to all children
  return React.Children.map(children, (child) =>
    React.cloneElement(child, { className: "highlighted" })
  );
}

// Usage
<Parent>
  <p>First</p>
  <p>Second</p>
</Parent>

Practice Problems

0/3solved
Build Component Composition Component

Create a reusable React component implementing Component Composition. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
Component Composition Testing

Write unit and integration tests for Component Composition using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
Component Composition Performance

Optimize Component Composition for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What is component composition?

Question 1 options

2. What is a render prop?

Question 2 options

3. What is the children prop?

Question 3 options

4. What replaced render props?

Question 4 options

Flashcards

Question

What is component composition?

Answer

Building complex UIs by combining smaller components

Question

What is a render prop?

Answer

A function prop that returns JSX

Question

What is the children prop?

Answer

A special prop containing nested elements

Question

What are compound components?

Answer

Components that work together using implicit state sharing

Question

What is Component Composition?

Answer

Component Composition is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.Composition is preferred over inheritance
  • 2.Render props share logic via function props
  • 3.Children prop enables component nesting
  • 4.Custom hooks replaced render props
  • 5.Compound components share implicit state

Interview Tips

  • Explain composition vs inheritance
  • Show render prop pattern
  • Demonstrate children prop usage

Cheat Sheet

Cheat Sheet

Composition

function Layout({ children }) {
  return <div>{children}</div>;
}

Render Props

<DataFetcher
  render={({ data, loading }) => (
    loading ? <Spinner /> : <List data={data} />
  )}
/>

Children as Function

<Toggle>
  {({ on, toggle }) => (
    <button onClick={toggle}>{on ? "ON" : "OFF"}</button>
  )}
</Toggle>

React.Children

React.Children.map(children, fn)
React.Children.count(children)
React.cloneElement(child, props)