React.js Best Practices & Patterns -Part 2

Bouazza Ayyoub
7 min readAug 28, 2023

--

Separation of concerns for high-quality applications

Introduction:

This article is part of a series of three articles that review some of the best techniques and patterns for working with React.js. In this article, we will delve into topics such as custom hooks and services, and determine the most suitable implementation approach for each. These techniques are designed to enhance the scalability and maintainability of your code. Throughout the article, you will find code snippets and valuable tips to effectively incorporate these best practices into your projects.

📝Feel free to explore the initial segment of this article — go ahead and give it a read!

Separation of Concerns

In software development, concern separation is critical. It’s all about ensuring that each component of your program serves a single purpose. Separating the logic from the view in React.js can help make your code more maintainable, reusable, and scalable. So let’s look at some examples and approaches to help us create this separation and improve our React projects.

1. Custom Hooks

Custom hooks allow you to extract stateful logic from your components and apply it to multiple components. Custom hooks are just JavaScript functions with a unique name convention: they must begin with “use” (for example, useForm). These hooks can be used in conjunction with other built-in or custom hooks, and they can export an object or an array containing all of the attributes and methods that you want to use in your components.

let’s see some examples to understand how custom hooks might improve your React.js code:

🔍 Example 2: Custom Hook for Form Input Handling

Consider a form with numerous input fields. Handling the status and input for each field can become tedious and time-consuming. But don’t worry, we can simplify things with a custom hook named useForm. This hook allows you to extract the state and logic for all of those fields while keeping things tidy.

First, we build a values object using useState to hold all of our form input values. Then, whenever an input changes, we call the handleChange method, which updates the values object. Finally, we include a resetForm method that resets everything to zero.

We can utilize these attributes and methods in all of our components by putting values, handleChange, and resetForm in an array. This makes our form component much easier to maintain and reuse.

Now, we can use the useForm custom hook in our form component:

As you see, the useForm custom hook helps us handle form inputs with ease.

Let’s take a look at another example.

🔍 Example 2: Custom Hook for Fetching Data

You can also use custom hooks to retrieve data from an API. It’s actually quite common(you can see ReactQuery). All we need to do is create a custom hook called useFetch that handles the state and side effects of fetching data. Sahla-Mahla(Easy-peasy)!

Now, you can use the useFetch custom hook in any component that requires data from an API:

Now, we can simply handle the state and logic for fetching data from the view in our UserList component thanks to the useFetch custom hook. This greatly simplifies the maintenance and reuse of our code. We can maintain all of the state and side effects associated with fetching data in one place with useFetch, then use the generated data in different components. Because of this separation of responsibilities, we can quickly update our data-fetching mechanism without affecting the components that rely on it.

Overall, it improves the maintainability and scalability of our code.

2. Services

Using services to make your React projects more modular is a smart way to go. Services are methods or classes that conduct business logic such as calling APIs, manipulating data, and other useful tasks. These services can be imported and used in your components or custom hooks. It’s a simple technique to keep things tidy by separating logic from view.

Let’s take a look at an example to gain a better understanding.

🔍 Example 1: API Service

Assume we need to use a RESTful API to do certain CRUD operations on user data. To make things easier, we can develop a service that handles everything. It’s a convenient way to interface with the API and complete our jobs quickly.

Now, we can use the API service in our components or custom hooks:

We can keep all of the API interaction logic separate from the view in our UserList component thanks to the API service. In this manner, we can easily maintain and reuse our code. It’s a terrific approach to keep everything organized and manageable.

One more example wouldn’t hurt.

🔍Example 2: Utility Service

Assume we need to accomplish something useful, such as formatting dates, calculating sums, or generating unique IDs. We can design a useful utility service that will take care of everything. It’s like having our own personal assistant that we can call on whenever we need to get some meaningful work done.

Cool! We can easily use our utility service in our components or custom hooks to make our code more efficient and reusable. It’s a great way to keep things organized and make sure everything runs smoothly.

By using the utility service, we’ve kept the utility logic separate from the view, making our TransactionList component easier to manage and reuse.

3. When to use a Custom Hook and when to use a Service Function

It is critical to analyze the individual use case and needs of your application when determining whether to utilize a custom hook or a service function. Custom hooks are more suited for managing state or context or for reusing functionality across several components. Service functions, on the other hand, are better suited for reusable logic that does not depend on state or context.

In practice, you may find that some functionality fits better as a custom hook, while other functionality works better as a service function. For example, here are some more examples of when you might choose to use each approach:

Custom Hooks:

  • Managing complex state: If you find yourself needing to manage complex state across multiple components, it’s recommended to utilize Custom hooks. For instance, if you’re tasked with overseeing a user’s preferences and correlating them with your product, a custom hook such as ‘useUserSettings’ could prove invaluable. This hook would assist in keeping track of the user’s preferences state and offer functionality to validate their compatibility with your product.
  • Context-dependent functionality: A custom hook can assist in accessing context from different components and offering reusable functionality. For instance, if you need to manage user authentication across multiple components, a custom hook like ‘useLoggedUser’ could enable you to access the user authentication state and provide the capability to manage or update it from other components.

Service Functions:

  • Task-specific functionality: If you have a task that is unrelated to context or state management, employing a service function can be an effective strategy. For instance, when there’s a need to validate data such as email or password or perform certain formatting operations, service functions can offer these functionalities without necessitating context or state involvement.
  • Standalone functionality: If you intend to offer functionality that can be utilized across various applications or platforms, employing a service function is a valuable approach for encapsulating and ensuring the reusability of that logic. For instance, whether you require a function for image processing, file storage, email transmission, or payment processing, service functions can deliver these capabilities in a manner conducive to their application across a diverse range of applications or platforms.

Conclusion:

In conclusion, separation of concerns is a principle used in programming to separate an application into units, with minimal overlapping between the functions of the individual units. To achieve this in React.js applications you could use custom hooks or service functions.
Alternatively, neglecting to apply utilize separation of concerns principle could result in code that becomes less maintainable and organized, often leading to what’s commonly referred to as “code smells.” 🤢

Thank you for reading! Clap for this article as much as you can.

📩 For similar content, feel free to follow me and subscribe to my newsletter.
👋 You can say hello here.

--

--

Bouazza Ayyoub

I believe in using technology as a positive force for social good. I am a software engineer, IT lover with a passion to change the self and environment.