5 React Design Patterns You Should Know 🤔
Are you tired of writing the same code over and over again? Want to take your React skills to the next level? Discover the power of HOCs, Render Props, Hooks, and Context API; these five mind-blowing React design patterns will change how you think about coding!
1. Container and Presentational Components 🔥
The first pattern we’ll look at is separating the container and presentational components. This pattern is about separating data management concerns and UI rendering. Container components are responsible for managing the data and state of the application, while presentational components are responsible for rendering the UI.
Here’s an example of a container component:
import { useState } from 'react';
function TodoContainer() {
const [todos, setTodos] = useState([]);
function handleAddTodo(todo) {
setTodos([...todos, todo]);
}
return (
<TodoPresentational
todos={todos}
onAddTodo={handleAddTodo}
/>
);
}
And here’s an example of a presentational component:
function TodoPresentational({ todos, onAddTodo }) {
return (
<>
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
<button onClick={() => onAddTodo({ id: Date.now(), text: 'New Todo' })}>
Add Todo
</button>
</>
);
}
By separating data management concerns and UI rendering, our code becomes more modular and easier to understand.
2. Higher-Order Components (HOCs) 🚀
A Higher-Order Component (HOC) is a pattern that allows you to reuse component logic. HOCs are a way to share standard functionality among multiple components.
Here’s an example of a HOC that adds a loading state to a component:
import { useState, useEffect } from 'react';
function withLoading(WrappedComponent) {
return function LoadingComponent({ isLoading, ...props }) {
const [loading, setLoading] = useState(isLoading);
useEffect(() => {
setLoading(isLoading);
}, [isLoading]);
if (loading) {
return <div>Loading...</div>;
}
return <WrappedComponent {...props} />;
};
}
You can use this HOC to add a loading state to any component:
const TodoListWithLoading = withLoading(TodoList);
function App() {
const [isLoading, setIsLoading] = useState(false);
return (
<>
<button onClick={() => setIsLoading(true)}>
Load Todos
</button>
<TodoListWithLoading isLoading={isLoading} />
</>
);
}
By using HOCs, we can easily add common functionality to multiple components without duplicating code.
3. Render Props 🎨
The Render Props pattern is similar to HOCs, but instead of using a higher-order function to wrap a component, it uses a prop to pass a function that a component can use to render its content.
Here’s an example of a component that uses a render prop to toggle the visibility of its content:
function Toggle({ children }) {
const [isVisible, setIsVisible] = useState(false);
function handleClick() {
setIsVisible(!isVisible);
}
return children({
isVisible,
toggle: handleClick
});
}
And here’s an example of how to use the `Toggle` component:
function App() {
return (
<Toggle>
{({ isVisible, toggle }) => (
<>
<button onClick={toggle}>Toggle</button>
{isVisible && <p>This content is visible</p>}
</>
)}
</Toggle>
);
}
The Render Props pattern allows for a more flexible way to share component logic.
4. Hooks 🎣
Hooks are a way to use state, and other React features in functional components. They were introduced in React 16.8 as an alternative to class-based components.
Here’s an example of how to use the useState hook:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increase
</button>
</>
);
}
Hooks make it easy to use state and other features in functional components and make it easier to write and understand the code.
5. Context API 📚
The Context API passes data down to components without passing props through multiple component levels. It allows you to create a context and a provider that can be used to access the data from any component in the tree.
Here’s an example of how to create a context and a provider:
import { createContext, useState } from 'react';
export const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
And here’s an example of how to use the context in a component:
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
function ThemeToggler() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<>
<p>Current theme: {theme}</p>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</>
);
}
Using the Context API, we can easily share data across multiple components without passing props through multiple levels.
You can write more efficient, elegant, and maintainable code by understanding and utilizing these five design patterns, including HOCs, Render Props, Hooks, and Context API. They are not the only solution for every problem but are a good starting point for solving common issues. So, don’t wait any longer; start mastering these patterns today and take your React skills to the next level! 🚀