Definition
Separation of concerns is not a new concept, nor is it peculiar to react applications alone.
In computer science, separation of concerns is a design principle for separating a computer program into distinct sections, each section addressing a set of information that affects the code of a computer program. Separation of Concerns lies in the foundations of software development. It is one of the most beneficial principles since it leads to encapsulation and modularity, leading to flexible and maintainable applications.
In react applications, separation of concerns is a design principle that involves the separating of the UI from the business logic
Benefits of separation of concern
- Reuse logic: Logic that is now independent of a UI can be reused throughout the application and not duplicated across components.
- Ease of testing: Separating makes it clearer to detect problems and focus on solutions.
Avoids tight coupling: Tight coupling in a program occurs when functions and chunks of code that can be independent of each other are highly dependent on each other, in which a change to one part of the code can cause other parts of the code to not function properly.
Separation of concern in your react applications prevents tight coupling of functionalities.
Scalability: When there is a separation of concern in your react application, there is always an easier possibility to scale your small-scoped application.
Principles for separating concerns in react applications
- Separate your CSS from JSX: keep them in separate files
- Single responsibility principle: The idea behind the SRP is that every class, module, or function in a program should have one responsibility/purpose in a program. As a commonly used definition, "every class should have only one reason to change". Therefore, keep this principle in mind as you build your react components.
- Design patterns: Since we will split our components into smaller components, we need some efficient way to handle their relations; For example, making use of the provider pattern will solve the props drilling issue, and so on.
- Split logic: Keep your UI separate from the business logic
Split Logic: Keep UI separate from the business logic
Separating business logic from the UI can be done in any of the following ways:
- Create a helper or utility file that contains the business logic
- Create a custom hook
- Use global state management, such as context API, redux etc
Let's look at the following Counter component that has two buttons - increase and decrease and a corresponding logic that changes the value being displayed based on the button clicked. We are going to follow the separation of concerns principle to make our code more modular.
import { useState } from "react";
const Counter = () => {
const [count, setCount] = useState(100);
const increaseCount = () => setCount(count + 1);
const decreaseCount = () => setCount(count - 1);
return (
<div>
<p>{count}</p>
<div>
<button style={{marginRight: '25px'}} onClick={increaseCount}>Increase</button>
<button onClick={decreaseCount}>Decrease</button>
</div>
</div>
);
};
export default Counter;
This is a very simple code but it gives us the chance to practice separation of concerns.
First, we would extract our style from the code, and we will be left with this:
import { useState } from "react";
const Counter = () => {
const [count, setCount] = useState(100);
const increaseCount = () => setCount(count + 1);
const decreaseCount = () => setCount(count - 1);
return (
<div>
<p>{count}</p>
<div>
<button className='btn-increase' onClick={increaseCount}>Increase</button>
<button onClick={decreaseCount}>Decrease</button>
</div>
</div>
);
};
export default Counter;
The button now has a className and the style has been described in a styles file
// ./styles.scss
.btn-increase{
margin-right:25px;
}
Next, we separate the business logic from the UI. We do this by creating a custom hook useCounter
// ./useCounter.js
import { useState } from "react";
const useCounter = () => {
const [count, setCount] = useState(100);
const increaseCount = () => setCount(count + 1);
const decreaseCount = () => setCount(count - 1);
return {
count,
increaseCount,
decreaseCount
};
};
export default useCounter;
Our counter component file now looks like this:
import useCounter from "./useCounter";
const Counter = () => {
const {count, increaseCount, decreaseCount} = useCounter()
return (
<div>
<p>{count}</p>
<div>
<button className='btn-increase' onClick={increaseCount}>Increase</button>
<button onClick={decreaseCount}>Decrease</button>
</div>
</div>
);
};
export default Counter;
Our UI display is now independent of the business logic.
This is a very simple example but if our app was so much larger, this principle can abstract a lot of the business logic in the application.
Happy coding and don't be afraid to split components into smaller components ๐ต