Introduction
I am a software engineer. I love coding, and I know how to code—I’m actually quite good at it. I know how to code and I use most design patterns automatically, but I honestly do not know what they are called (with a few exceptions).
Here are some common design patterns explained with React code:
Singleton Pattern
Ensures a class has only one instance.
Example in React: A global state management instance (e.g., Redux store, Context API provider).
const StoreSingleton = (function () {
let instance;
function createInstance() {
return { user: null, theme: "dark" };
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
},
};
})();
const store = StoreSingleton.getInstance();
Factory Pattern
Creates objects without specifying the class.
Example in React: Dynamic component rendering based on props.
const ButtonFactory = ({ type, label }) => {
const components = {
primary: (label) => <button className="btn btn-primary">{label}</button>,
secondary: (label) => <button className="btn btn-secondary">{label}</button>,
};
return components[type] ? components[type](label) : <button>{label}</button>;
};
// Usage
<ButtonFactory type="primary" label="Click Me" />;
Observer Pattern
Notifies dependent objects when one changes.
Example in React: Event listeners, pub-sub mechanism, React Context API.
import { useState, useEffect } from "react";
const eventListeners = new Set();
const subscribe = (listener) => eventListeners.add(listener);
const unsubscribe = (listener) => eventListeners.delete(listener);
const notifyAll = (data) => eventListeners.forEach((listener) => listener(data));
const useNotification = () => {
const [message, setMessage] = useState(null);
useEffect(() => {
const listener = (data) => setMessage(data);
subscribe(listener);
return () => unsubscribe(listener);
}, []);
return message;
};
// Usage
const NotificationDisplay = () => {
const message = useNotification();
return message ? <p>{message}</p> : null;
};
Decorator Pattern
Dynamically adds features without modifying core code.
Example in React: Higher-Order Components (HOCs) & Custom Hooks.
const withLogging = (WrappedComponent) => {
return (props) => {
console.log("Component Rendered:", WrappedComponent.name);
return <WrappedComponent {...props} />;
};
};
const Button = (props) => <button {...props}>{props.label}</button>;
const LoggedButton = withLogging(Button);
// Usage
<LoggedButton label="Click Me" />;
Adapter Pattern
Converts incompatible interfaces for seamless interaction.
Example in React: Converting an API response to match UI needs.
const legacyAPIResponse = { fname: "John", lname: "Doe" };
const adaptUser = (data) => ({
firstName: data.fname,
lastName: data.lname,
});
const UserProfile = ({ user }) => {
const adaptedUser = adaptUser(user);
return <p>{adaptedUser.firstName} {adaptedUser.lastName}</p>;
};
// Usage
<UserProfile user={legacyAPIResponse} />;
Strategy Pattern
Encapsulates algorithms for flexible swapping.
Example in React: Using different sorting algorithms in a table component.
const strategies = {
ascending: (arr) => [...arr].sort(),
descending: (arr) => [...arr].sort().reverse(),
};
const SortableList = ({ items, strategy }) => {
const sortedItems = strategies[strategy](items);
return <ul>{sortedItems.map((item) => <li key={item}>{item}</li>)}</ul>;
};
// Usage
<SortableList items={["Banana", "Apple", "Cherry"]} strategy="ascending" />;
Command Pattern
Wraps requests as objects to allow undo/redo.
Example in React: Undo/redo for a text editor.
import { useState } from "react";
const useCommand = () => {
const [history, setHistory] = useState([]);
const [index, setIndex] = useState(-1);
const execute = (command) => {
const newHistory = history.slice(0, index + 1);
newHistory.push(command);
setHistory(newHistory);
setIndex(newHistory.length - 1);
return command.execute();
};
const undo = () => {
if (index >= 0) {
const command = history[index];
setIndex(index - 1);
return command.undo();
}
};
return { execute, undo };
};
Composite Pattern
Treats objects and compositions the same way.
Example in React: Recursive component rendering.
const Comment = ({ text, replies }) => (
<div>
<p>{text}</p>
{replies && replies.map((reply, i) => <Comment key={i} {...reply} />)}
</div>
);
Conclusion
I use most of these design patterns daily—but I don’t know what they are called. I know I don’t like a services pattern (oh sorry, the Facade pattern) as I like keeping all my fetching code together in my Context. Though, to be honest, I am starting to wonder why.