UI Components Doing Too Much
One of the common mistakes in frontend development is the failure to separate business logic from component logic. Components should primarily focus on the user interface. They should only contain simple code and logic related to data display and state management.
Although a component may need to make an API call on initial render to fetch the necessary information, it's better to abstract the API call logic by moving the interaction to a separate service. This provides several key benefits:
-
Task Separation: By separating business logic and component logic, you simplify each one. Components deal exclusively with display and user input handling, while business logic handles data processing and API interactions. This also makes it easier to test components since each can be tested in isolation from the business logic.
-
Reusability: Business logic, when moved to separate modules, can be easily reused in other parts of the application. This reduces code duplication and makes it easier to maintain. Additionally, it simplifies making changes to the business logic without altering the UI components.
UI components can become quite complex due to the logic needed to display various parts of the interface. Adding business logic makes the component even more complicated, making the code harder to understand and maintain.
An example of the correct approach is using React Hooks or contexts to manage state and effects within functional components while moving business logic to separate functions or classes. For instance, useEffect
can be used to perform side effects like API calls, and useState
to manage the component's local state.
Example
Let's consider a simple example of a component that should display a list of users fetched from a server using a fetch
request. First, let's take a look at the version with non-separated business logic. Here, the component is responsible not only for displaying the list of users but also for making the API call:
To improve the code structure, we will move the data-fetching logic (API call) to a separate service module, the userService.ts
file:
Now our familiar component uses the logic moved to the service to fetch the data:
In this improved example, the component focuses on displaying the data and managing its own state, while all business logic for fetching data is moved to a separate service. As the saying goes, each of them now minds its own business.
This approach helps make the code cleaner, more modular, and easier to maintain. It simplifies testing, reuse, and code changes. By following these principle, you can significantly improve the quality and maintainability of your frontend application.