React Design Patterns — Simplified
As React developers, understanding design patterns is key to writing clean, maintainable, and efficient code. In this post, we’ll delve into a crucial design pattern that can significantly improve how you structure your React applications. This pattern focuses on proper component and state organization, promoting separation of concerns and reusability — making it virtually impossible to write bad code once mastered.

Why This Design Pattern Matters
When developers start using React, it’s common to fall into the trap of writing components that are bloated, with unrelated state variables and functionality crammed together. This results in messy, hard-to-maintain code. Unfortunately, this is the reality in many React projects. But understanding how to refactor this kind of code using smart design patterns will dramatically change the way you approach building applications.
This particular pattern, which we’ll walk through here, is focused on breaking down components into more manageable pieces. It helps avoid common pitfalls like the “wall of state” or components doing too many things at once, which violates best practices like the Single Responsibility Principle (SRP).
Breaking Down the Design Pattern

In many projects, developers will encounter components that hold numerous state variables and functions, many of which are unrelated. Here’s an example:
const App = () => {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [isDarkMode, setIsDarkMode] = useState(false);
const incrementCount = () => setCount(count + 1);
const handleNameChange = (e) => setName(e.target.value);
const toggleDarkMode = () => setIsDarkMode(!isDarkMode);
return (
<div>
{/* JSX to render the UI */}
</div>
);
};
This component is doing too many things: managing state for a counter, user input, and dark mode toggle. Instead of piling everything into one place, we can split the logic into separate, more focused components or custom hooks.
Applying the Design Pattern: Refactoring
Step 1: Splitting the State
The first step in applying this design pattern is to break up the state variables into smaller, more relevant components. For instance, the `count` state and its logic can be isolated into its own custom hook, making it reusable across other components. Here’s how you can do it:
const useCount = () => {
const [count, setCount] = useState(0);
const increment = useCallback(() => setCount(prev => prev + 1), []);
return { count, increment };
};
NB: adding in useCallback will improve the performance as this hooks can be later be used inside other components and the user doesn’t need to wrap the functions elsewhere.
Now, instead of managing the count in the `App` component, we can simply call this custom hook whenever needed, making our code cleaner and reusable:
const Counter = () => {
const { count, increment } = useCount();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
Step 2: Handling User Input

Similarly, user inputs like name and email can be refactored into a `UserForm` component:
const UserForm = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
return (
<form>
<input value={name} onChange={(e) => setName(e.target.value)} placeholder="Name" />
<input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" />
</form>
);
};
By moving the form logic out of the main component, we keep concerns separated and make it easier to maintain.
Step 3: Managing Dark Mode
The logic for toggling dark mode can also be extracted into its own component or custom hook:
const useDarkMode = () => {
const [isDarkMode, setIsDarkMode] = useState(false);
const toggleDarkMode = () => setIsDarkMode(prev => !prev);
return { isDarkMode, toggleDarkMode };
};
const DarkModeToggle = () => {
const { isDarkMode, toggleDarkMode } = useDarkMode();
return (
<button onClick={toggleDarkMode}>
{isDarkMode ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
</button>
);
};
Why This Matters
By refactoring our code this way, we’ve reduced the size and complexity of the main `App` component. It now serves its intended purpose as a high-level container that pulls together smaller, more focused components. Each part of the application has a single responsibility, adhering to React best practices.
Benefits of This Design Pattern
1. Reusability: Custom hooks like `useCount` can be reused across the entire app or even in different projects.
2. Maintainability: Smaller components and functions are easier to test, debug, and update.
3. Separation of Concerns: Each component or function only does one thing, making the app more modular.
4. Performance: Hooks like `useCallback` help optimize performance by preventing unnecessary re-renders.
Final Thoughts
In React, it’s easy to write messy code, especially when starting out. But with the right design pattern in place, you can make your components scalable, efficient, and easier to maintain. Applying this pattern will elevate the quality of your code, ensuring that your React applications remain clean and well-structured.
If you’re interested in diving deeper into React design patterns, I will continuing my react design pattern series. Stay Tuned.