DEV Community

Zahid Hasan
Zahid Hasan

Posted on

Single Responsibility Principle in React

What is the Single Responsibility Principle?

The Single Responsibility Principle (SRP) is a software design principle that asserts a software module or component should only change for one cause. In other words, a class or function should only have one clear duty or job. Following this idea might assist in making your code more manageable and understandable.

How can you implement SRP in React?

The SRP may be implemented in React by ensuring that each component in your application serves a single, well-defined purpose. A component, for example, maybe in charge of displaying a specific section of the UI, processing user input, or performing API calls to retrieve data. You may make your code easier to comprehend and maintain by restricting a component’s duties to a single, well-defined job.

Here are a few points for applying the SRP in a React application:

  1. Keep your components small and focused: As far as possible, keep your components small and focused, with each component having a single, well-defined task.
  2. Avoid mixing concerns: Avoid combining unrelated problems in a single component. A component responsible for displaying a form, for example, should not simultaneously be responsible for performing API calls to get data.
  3. Use composition: Composition is a technique for combining fewer, more concentrated components into bigger, more complicated components. This might assist in making your code modular and understandable.
  4. Use props and state appropriately: Props may be used to transmit data and behavior down to child components, while the state can be used to maintain the local state within a component. Use state to handle data that is not exclusive to a single component.

Here’s an example of a React component that violates the Single Responsibility Principle:

import React, { useState } from 'react';
import { ApiService } from './Service/Api';

// This component has multiple, unrelated responsibilities:
// rendering a form, making API calls, and displaying a 
// loading spinner.
function AddItemForm() {
  const [item, setItem] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleChange = (event) => {
    setItem(event.target.value);
  }

  const handleSubmit = async (event) => {
    event.preventDefault();
    setIsLoading(true);
    try {
      await ApiService.SendItem({item})
      setIsLoading(false);
    } catch (error) {
      setIsLoading(false);
      setError(error);
    }
  }

  return (
    <div>
      { isLoading && <div>Loading...</div> }
      { error && <div>{error.message}</div> }
      <form onSubmit={handleSubmit}>
        <label>
          Add Item:
          <input type="text" value={item} onChange={handleChange} />
        </label>
        <button type="submit">Add</button>
      </form>
    </div>
  );
}

export default AddItemForm;
Enter fullscreen mode Exit fullscreen mode

This component is responsible for a variety of unconnected tasks, including rendering a form, making API calls to add a new item to the list, and showing a loading spinner and error warnings. These tasks are unrelated and may vary for a variety of reasons, which contradicts the Single Responsibility Principle.

To make this component more manageable and understandable, these tasks should be separated into independent components. You could, for example, design a separate component to handle API requests and another to display the loading spinner and error warnings. This would make the code easier to comprehend as well as test and maintain.

You might divide the tasks of producing a form, executing API calls, and showing a loading spinner and error messages into multiple components to make the AddItemForm component comply with the Single Responsibility Principle. Here’s an example of refactoring the component:

import React, { useState } from 'react';
import LoadingSpinner from './LoadingSpinner';
import ErrorMessage from './ErrorMessage';
import AddItemForm from './AddItemFrom.js'
import { ApiService } from './Service/Api';

// This component has a single, well-defined responsibility:
// making API calls to add a new item to the list.
function AddItemFormContainer() {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const addItem = async (item) => {
    setIsLoading(true);
    try {
      await ApiService.SendItem({item})
      setIsLoading(false);
    } catch (error) {
      setIsLoading(false);
      setError(error);
    }
  }

  return (
    <div>
      { isLoading && <LoadingSpinner /> }
      { error && <ErrorMessage error={error} /> }
      <AddItemForm addItem={addItem} />
    </div>
  );
}

export default AddItemFormContainer;
Enter fullscreen mode Exit fullscreen mode
import React, { useState } from 'react';

// This component has a single, well-defined responsibility:
// rendering a form to add a new item to a list.
function AddItemForm(props) {
  const [item, setItem] = useState('');

  const handleChange = (event) => {
    setItem(event.target.value);
  }

  const handleSubmit = (event) => {
    event.preventDefault();
    props.addItem(item);
    setItem('');
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Add Item:
        <input type="text" value={item} onChange={handleChange} />
      </label>
      <button type="submit">Add</button>
    </form>
  );
}

export default AddItemForm;
Enter fullscreen mode Exit fullscreen mode

The useState hook is used to handle the local state within the AddItemForm and AddItemFormContainer components in this version. The AddItemForm component has one state variable, item, while the AddItemFormContainer component has two, isLoading and error. The useState hook is used to initialize these state variables and the accompanying update functions, which are used to store and update the values of the loading spinner and error message.

The components are simple to comprehend and manage because they adhere to the Single Responsibility Principle and confine the responsibility of these components to a single, well-defined activity.

Top comments (0)