Topic

Mastering Advanced Component Patterns in React

Discover the hidden power of React’s flexibility and elevate your UI component game! In this in-depth exploration, we’ll dive into four expert-level patterns that will transform your coding skills:

  • Compound Components: Build modular, reusable UI elements
  • Render Props: Harness the power of flexible, dynamic rendering
  • Higher-Order Components (HOCs): Wrap your components with reusable logic
  • Controlled vs. Uncontrolled Components: Master the art of state management

Get ready to revolutionize your React development workflow and create stunning, efficient UI components that leave a lasting impression!

1. Compound Components

Compound Components enable the creation of components that work together seamlessly. They allow the parent component to provide context and control, while child components communicate with it.

Example: Tabs Component
This example demonstrates a Tabs component that allows switching between different panels. Each tab and panel is a child component that communicates with the parent Tabs component via context.
				
					import React, { useState, createContext, useContext } from 'react';

const TabsContext = createContext();

function Tabs({ children, defaultActiveTab }) {
  const [activeTab, setActiveTab] = useState(defaultActiveTab);
  
  return (
    <TabsContext.Provider value={{ activeTab, setActiveTab }}>
      <div className="tabs">{children}</div>
    </TabsContext.Provider>
  );
}

function TabList({ children }) {
  return <div className="tab-list">{children}</div>;
}

function Tab({ children, name }) {
  const { activeTab, setActiveTab } = useContext(TabsContext);
  const isActive = activeTab === name;

  return (
    <button
      className={`tab ${isActive ? 'active' : ''}`}
      onClick={() => setActiveTab(name)}
    >
      {children}
    </button>
  );
}

function TabPanels({ children }) {
  return <div className="tab-panels">{children}</div>;
}

function TabPanel({ children, name }) {
  const { activeTab } = useContext(TabsContext);
  return activeTab === name ? <div className="tab-panel">{children}</div> : null;
}

export { Tabs, TabList, Tab, TabPanels, TabPanel };
				
			

Usage:

				
					<Tabs defaultActiveTab="1">
  <TabList>
    <Tab name="1">Tab 1</Tab>
    <Tab name="2">Tab 2</Tab>
  </TabList>
  <TabPanels>
    <TabPanel name="1">Content for Tab 1</TabPanel>
    <TabPanel name="2">Content for Tab 2</TabPanel>
  </TabPanels>
</Tabs>
				
			

 

2. Render Props

Render Props is a pattern where a function is passed as a prop to a component. It provides flexibility in rendering logic.

Example: Mouse Tracker
This example creates a MouseTracker component that tracks the mouse position and uses a render prop to display the data in a customizable way.
				
					function MouseTracker({ render }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  function handleMouseMove(event) {
    setPosition({ x: event.clientX, y: event.clientY });
  }

  return (
    <div style={{ height: '100vh' }} onMouseMove={handleMouseMove}>
      {render(position)}
    </div>
  );
}

export default MouseTracker;
				
			

Usage:

				
					<MouseTracker render={({ x, y }) => (
  <h1>Mouse position: ({x}, {y})</h1>
)} />

				
			

 

3. Higher-Order Components (HOCs)

HOCs are functions that take a component and return a new component, enhancing functionality.

Example: Authentication HOC
This example creates an HOC withAuth that wraps a component and ensures only authenticated users can access it. If the user is not authenticated, it displays a login message.

				
					function withAuth(WrappedComponent) {
  return function AuthenticatedComponent(props) {
    const isAuthenticated = !!localStorage.getItem('authToken');

    if (!isAuthenticated) {
      return <div>Please log in to access this page.</div>;
    }

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

export default withAuth;
				
			

Usage:

				
					function Dashboard() {
  return <h1>Welcome to the Dashboard!</h1>;
}

export default withAuth(Dashboard);
				
			

 

4. Controlled vs. Uncontrolled Components

Controlled components are fully controlled by React state, while uncontrolled components rely on the DOM.

Example 1:
Controlled component
This example shows a controlled input field where the value is managed by React state.
				
					function ControlledInput() {
  const [value, setValue] = useState('');

  return (
    <input
      type="text"
      value={value}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}
				
			
Example 2: Uncontrolled components
This example demonstrates an uncontrolled input field where the value is accessed directly from the DOM using a ref.
				
					function UncontrolledInput() {
  const inputRef = useRef();

  function handleClick() {
    alert(inputRef.current.value);
  }

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleClick}>Show Value</button>
    </div>
  );
}
				
			

 

Conclusion

These advanced React patterns enhance component reusability and maintainability. By understanding and applying them, you can create more robust and scalable applications.

Experiment with these patterns and find the ones that best suit your projects!

Spread the Knowledge!