✈️ Design Patterns in React (2025) πŸ“ˆ

Design patterns provide standardized solutions to common problems in software development. πŸ…

The Developer Life
5 min read1 day ago

Introduction

In React, leveraging design patterns can lead to more scalable, maintainable, and efficient applications. This article delves into some essential design patterns in React, including the Container and Presentational Components pattern, the Higher-Order Component (HOC) pattern, the Render Props pattern, and the Custom Hook pattern. Each pattern will be illustrated using modern React hooks.

1. Container and Presentational Components Pattern

Concept: This pattern separates components into two categories: container components (smart components) that handle state and logic, and presentational components (dumb components) that handle rendering UI.

Benefits:

  • Separation of Concerns: Keeps business logic separate from UI rendering.
  • Reusability: Presentational components can be reused across different parts of the application.

Implementation:

Container Component:

// TodoContainer.js
import React, { useState, useEffect } from 'react';
import TodoList from './TodoList';

const TodoContainer = () => {
const [todos, setTodos] = useState([]);
useEffect(() => {
fetch('/api/todos')
.then(response => response.json())
.then(data => setTodos(data));
}, []);
return <TodoList todos={todos} />;
};
export default TodoContainer;

Presentational Component:

// TodoList.js
import React from 'react';

const TodoList = ({ todos }) => (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
export default TodoList;

2. Higher-Order Component (HOC) Pattern

Concept: A Higher-Order Component is a function that takes a component and returns a new component with additional props or behavior.

Benefits:

  • Code Reusability: Encapsulates common behavior that can be shared across multiple components.
  • Enhancement: Allows enhancing components with additional functionality.

Implementation:

// withLogging.js
import React, { useEffect } from 'react';

const withLogging = WrappedComponent => {
return props => {
useEffect(() => {
console.log('Component mounted');
}, []);
return <WrappedComponent {...props} />;
};
};
export default withLogging;
// MyComponent.js
import React from 'react';
import withLogging from './withLogging';

const MyComponent = () => <div>Hello, World!</div>;
export default withLogging(MyComponent);

3. Render Props Pattern

Concept: This pattern involves using a prop that is a function to share code between components.

Benefits:

  • Flexibility: Provides a way to pass dynamic behavior to components.
  • Reusability: Promotes code reuse by passing functions as props.

Implementation:

// MouseTracker.js
import React, { useState } from 'react';

const MouseTracker = ({ render }) => {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouseMove = event => {
setPosition({
x: event.clientX,
y: event.clientY
});
};
return (
<div style={{ height: '100vh' }} onMouseMove={handleMouseMove}>
{render(position)}
</div>
);
};
export default MouseTracker;
// App.js
import React from 'react';
import MouseTracker from './MouseTracker';

const App = () => (
<MouseTracker render={({ x, y }) => (
<h1>The mouse position is ({x}, {y})</h1>
)} />
);
export default App;

4. Custom Hook Pattern

Concept: Custom hooks allow you to encapsulate logic and state management into reusable functions.

Benefits:

  • Code Reusability: Extracts and reuses logic across multiple components.
  • Simplification: Simplifies component logic by moving complex logic into hooks.

Implementation:Copy code

// useFetchData.js
import { useState, useEffect } from 'react';

const useFetchData = url => {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => setData(data));
}, [url]);
return data;
};
export default useFetchData;
// DataDisplay.js
import React from 'react';
import useFetchData from './useFetchData';

const DataDisplay = ({ url }) => {
const data = useFetchData(url);
return data ? <pre>{JSON.stringify(data, null, 2)}</pre> : <p>Loading...</p>;
};
export default DataDisplay;

5. Compound Component Pattern

Concept: The Compound Component Pattern allows you to compose components together to form a cohesive unit where each component contributes to a larger purpose.

Benefits:

  • Encapsulation: Components work together seamlessly while each maintaining its own responsibility.
  • Flexibility: Users can customize the behavior and appearance of the compound component through its parts.

Implementation:

// Toggle.js
import React, { useState } from 'react';

const Toggle = ({ children }) => {
const [on, setOn] = useState(false);
const toggle = () => {
setOn(!on);
};
const getChildProps = () => {
return {
on,
toggle,
};
};
return <>{React.Children.map(children, child => React.cloneElement(child, getChildProps()))}</>;
};
export default Toggle;
// ToggleButton.js
import React from 'react';

const ToggleButton = ({ on, toggle }) => (
<button onClick={toggle}>{on ? 'Turn Off' : 'Turn On'}</button>
);
export default ToggleButton;
// ToggleMessage.js
import React from 'react';

const ToggleMessage = ({ on }) => (
<p>{on ? 'The button is ON' : 'The button is OFF'}</p>
);
export default ToggleMessage;
// App.js
import React from 'react';
import Toggle from './Toggle';
import ToggleButton from './ToggleButton';
import ToggleMessage from './ToggleMessage';
const App = () => (
<Toggle>
<ToggleButton />
<ToggleMessage />
</Toggle>
);
export default App;
  • Toggle Component: Manages the state (on or off) and provides a toggle function to its children through the Compound Component Pattern.
  • ToggleButton Component: Receives on state and toggle function as props to handle click events and display appropriate text.
  • ToggleMessage Component: Receives on state to display a message based on the current state.

This pattern allows ToggleButton and ToggleMessage to work together seamlessly under the management of Toggle, demonstrating the Compound Component Pattern's encapsulation and flexibility.

Conclusion

Understanding and implementing design patterns in React with hooks can significantly enhance the scalability, maintainability, and efficiency of your applications. By leveraging patterns like Container and Presentational Components, Higher-Order Components, Render Props, Compound Components and Custom Hooks, you can create modular and reusable code that adheres to best practices. Experiment with these patterns to see how they can improve your React projects.

--

--

No responses yet