DEV Community

Zahid Hasan
Zahid Hasan

Posted on

Open-Closed Principle In React

What is the Open-Closed Principle?

The Open Close Principle is a software design principle that stated that software entities (classes, modules, functions, etc.) should be extensible but not modifiable.

This means that you should be able to add new functionality to an existing software entity without modifying its code.

Assume you have a toy box filled with a variety of toys. The toy box functions similarly to a software entity. You may continue to add new toys to the toy box (expand it) without changing the toy box itself. However, you should not have to open the toy box and change its structure or construction every time you want to add a new toy (modifying it).

How can you implement OCP in React?

One way to implement the Open Close Principle in React is to use higher-order components (HOCs).

Functions that accept a component as an input and return a new component are examples of higher-order components. HOCs may be used to extend the functionality of a component without modifying its code.

Here’s an example of a React component that violates the Open-Closed Principle:

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      setLoading(true);
      try {
        const response = await axios.get('/api/users');
        setUsers(response.data);
      } catch (err) {
        setError(err);
      }
      setLoading(false);
    }
    fetchData();
  }, []);

  if (loading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Error: {error.message}</p>;
  }

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

This component displays a list of users fetched through an API call. It is divided into three states: loading, error, and users. When the component mounts, it retrieves data from the API and shows a loading spinner or an error message if there is a problem.

The issue with this solution is that the component is not extensible. If you want to add new functionality to the component, such as filtering the list of users by name, you must change the code. To incorporate the filter in the API request, you would need to add a new state variable for it and edit the useEffect hook.

To solve this while following the Open Closed Principle, you may divide the API functionality and state management into a distinct custom hook that can be readily extended without rewriting its code.

Here is the refactored version of the UserList the component that follows the Open Close Principle:

import React from 'react';
import useApi from './useApi';

function UserList() {
  const { data: users, loading, error } = useApi('/api/users');

  if (loading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Error: {error.message}</p>;
  }

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

The useApi the hook is a custom hook that manages API logic and state. It accepts a URL as an input and produces an object containing the data, loading, and error states.

The useApi a hook is implemented as follows:

import { useState, useEffect } from 'react';
import axios from 'axios';

function useApi(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      setLoading(true);
      try {
        const response = await axios.get(url);
        setData(response.data);
      } catch (err) {
        setError(err);
      }
      setLoading(false);
    }
    fetchData();
  }, [url]);

  return { data, loading, error };
}

export default useApi;
Enter fullscreen mode Exit fullscreen mode

You can now simply add new features to the UserList component without having to modify its code. For example, you may use the useApi hook to add a filter to the list of users, as seen below:

import React from 'react';
import useApi from './useApi';

function UserList() {
  const [filter, setFilter] = useState('');
  const { data: users, loading, error } = useApi(`/api/users?name=${filter}`);

  if (loading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Error: {error.message}</p>;
  }

  return (
    <>
      <input type="text" value={filter} onChange={event => setFilter(event.target.value)} />
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

The UserList a component is now extendable, allowing you to add new features without modifying the code.

Top comments (0)