As React applications grow, organizing components and managing behavior becomes more complex. React provides several design patterns to help structure components in a scalable and maintainable way. These patterns serve as reusable solutions to common problems encountered in React development.
React patterns are established approaches to structuring components that enhance reusability and maintainability. They allow you to:
Two of the most popular patterns are Higher-Order Components (HOCs) and Render Props. These patterns enable component reuse while reducing duplication of code.
A Higher-Order Component (HOC) is a function that takes a component as an argument and returns a new component with added functionality. It’s essentially a wrapper around an existing component, providing additional behavior or data manipulation while keeping the original component’s interface intact.
HOCs are particularly useful when you need to share logic across multiple components without repeating the same code in each one. Examples include adding logging, handling API calls, authentication, or enhancing components with additional props.
Let’s start with a simple HOC that adds some data (in this case, a user object) to a wrapped component.
import React from 'react';
// Higher-Order Component that provides user data
const withUser = (WrappedComponent) => {
const user = { name: 'John Doe', age: 30 };
return (props) => {
return ;
};
};
// Presentational component that receives user data as props
const UserComponent = ({ user }) => {
return (
User Information
Name: {user.name}
Age: {user.age}
);
};
// Wrapping UserComponent with the HOC
const EnhancedUserComponent = withUser(UserComponent);
export default EnhancedUserComponent;
withUser
is the HOC that adds a user
prop to the wrapped component.UserComponent
is a presentational component that renders the user’s information based on the user
prop it receives.EnhancedUserComponent
is the result of applying the HOC to UserComponent
, providing it with additional data (user
).
User Information
Name: John Doe
Age: 30
The HOC pattern allows us to separate concerns. The UserComponent
is purely focused on rendering UI, while the withUser
HOC is responsible for providing the necessary data.
Now, let’s create a more advanced HOC that fetches data from an API and injects the data into the wrapped component.
import React, { useEffect, useState } from 'react';
// Higher-Order Component that fetches data from an API
const withData = (url) => (WrappedComponent) => {
return (props) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
if (loading) return Loading...
;
return ;
};
};
// Presentational component that displays fetched data
const DataDisplay = ({ data }) => {
return (
Fetched Data
{JSON.stringify(data, null, 2)}
);
};// Wrapping DataDisplay with HOC to fetch data from an API
const EnhancedDataDisplay = withData('https://jsonplaceholder.typicode.com/posts')(DataDisplay);export default EnhancedDataDisplay;
withData
is an HOC that accepts a URL and fetches data from the specified API.useState
and useEffect
hooks to manage state and fetch data.DataDisplay
is a presentational component that receives the fetched data and displays it.EnhancedDataDisplay
component is the result of applying the HOC, and it displays the API data when it is loaded.
Fetched Data
[
{
"userId": 1,
"id": 1,
"title": "Post Title 1",
"body": "Post body content..."
},
...
]
This example demonstrates how HOCs can be used for more complex tasks, such as fetching data, without mixing logic and presentation code.
The Render Props pattern allows you to share logic between components using a function as a prop. Instead of passing data as props, you pass a function that defines what to render. This function can contain all the necessary logic and decide how the component should behave.
Here’s a simple example where we pass a render function as a prop to a component that handles mouse movements.
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 (
{render(position)}
);
};
const App = () => (
Mouse Position: {x}, {y}
} />
);
export default App;
MouseTracker
is a component that tracks the mouse position using an event listener and state.App
, we pass a render function to MouseTracker
that renders the mouse position in an h2
element.
Mouse Position: 150, 300
The render function passed to MouseTracker
controls how the mouse position is displayed. This pattern provides flexibility for dynamic rendering.
Let’s take the render props pattern a step further by creating a reusable data-fetching component.
import React, { useEffect, useState } from 'react';
const DataFetcher = ({ url, render }) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
if (loading) return Loading...
;
return render(data);
};
const App = () => (
(
{data.map(post => - {post.title}
)}
)} />
);
export default App;
DataFetcher
is a reusable component that fetches data from an API and provides it to its children via a render function.render
prop allows the parent component to control how the data is displayed, making DataFetcher
highly flexible and reusable.
- Post Title 1
- Post Title 2
- Post Title 3
...
Both Higher-Order Components and Render Props are powerful patterns in React that help you build more modular, reusable, and flexible components. By abstracting common functionality and enabling flexible rendering, these patterns allow for cleaner code and better separation of concerns. Each pattern has its strengths, and choosing the right one depends on your application's specific needs. With these patterns in your toolkit, you can write more maintainable and scalable React applications. Happy Coding!❤️