Open-Closed Principle In React
Tags: webdev, react, javascript, programming
Published: 2023-01-04 18:57:18

**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:

```javascript
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>
  );
}
```

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:

```javascript
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>
  );
}
```
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:

```javascript
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;
```

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:

```javascript
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>
    </>
  );
}
```
The `UserList` a component is now extendable, allowing you to add new features without modifying the code.