Liskov Substitution Principle in React
Tags: react, javascript, programming, webdev
Published: 2023-01-13 15:47:29

**What is Liskov Substitution Principle?**

The Liskov Substitution Principle (LSP) is a guideline that assists in the effective functioning of the software. It states that if something is of a given kind, you should be able to substitute a different but similar item for it and the software would still function properly.

Consider a toy box filled with various sorts of toys such as vehicles, dolls, and balls. Because the toy box can only contain toys, whatever is placed within it must be a toy. According to the LSP, if you have a toy car, you should be able to replace it with a toy truck and the toy box should still function properly.

According to the LSP, objects of a superclass should be able to be substituted by objects of a subclass without impacting the program’s correctness.

**How can you implement Liskov Substitution Principle in React?**

Here are some ways to implement the Liskov Substitution Principle in React:

- Make your own hooks to specify the anticipated behavior and features of certain functionality, such as a form input. Any component that makes use of the custom hook should be able to be utilized whenever a form input is anticipated and the application should still function properly.
- Create custom hooks that expect a set of properties, such as an object containing the component’s state and behavior, then return the desired behavior and properties. As long as the items have the same form, this enables for easy substitution of various objects with varying actions.
- Use composition to define the expected behavior and properties of a component. Composing small and focused components together, it allows for easy substitution of related components that conform to the same interface and behavior.

Here’s an example of a scenario where the Liskov Substitution Principle is violated:

Assume you have a custom hook named `useAuth` that controls an application’s authentication state, such as the user’s token and login status.

```javascript
import { useState } from "react";

const useAuth = () => {
  const [token, setToken] = useState(null);
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const login = (token) => {
    setToken(token);
    setIsLoggedIn(true);
  }

  const logout = () => {
    setToken(null);
    setIsLoggedIn(false);
  }

  return { token, isLoggedIn, login, logout };
}
```

Suppose you have a component named `Profile` that renders the user’s information while they are signed in and a login button when they are not.

```javascript
const Profile = () => {
  const { token, isLoggedIn, login } = useAuth();

  if (!isLoggedIn) return <button onClick={() => login("pass")}>Login</button>;

  return <p>Welcome, you are logged in.</p>;
}
```

Imagine you want to add another component named Settings that allows the user to update their settings while they are logged in and a login button when they are not.

```javascript
const Settings = () => {
  const { token, isLoggedIn, login } = useAuth();

  if (!isLoggedIn) return <button onClick={() => login("pass")}>Login</button>;

  return (
    <div>
      <form>
        <label>Change your password</label>
        <input type="password" placeholder="New password" />
        <button>Save</button>
      </form>
    </div>
  );
}
```

As you can see, both the `Profile` and `Settings` components utilize the `useAuth` hook to manage the application’s authentication state, but because each component has distinct expectations for the hook, the `useAuth` hook is unable to handle the various cases. Because the `useAuth` hook cannot be used interchangeably in various components, this violates the LSP.

To address this issue, you could construct different hooks for different use cases, or you could develop a more general hook that can handle multiple circumstances by giving more parameters such as anticipated behavior or data.

Following this principle allows you to design a collection of interchangeable hooks, which makes it simple to add new features to your application because you can use the same hooks for other components and the application will still function appropriately.

Here is one solution to fix the issue of the `useAuth` hook violating the LSP:

Create a more general `useAuth` hook that can handle several cases by giving additional arguments, such as desired behavior or data.

```javascript
import { useState } from "react";

const useAuth = (initialAuth) =>{
  const [auth, setAuth] = useState(initialAuth);

  const login = (token) => {
    setAuth({ token, isLoggedIn: true });
  }

  const logout = () => {
    setAuth({ token: null, isLoggedIn: false });
  }

  return { auth, login, logout };
}
```

Pass the initial auth state as a parameter to the `useAuth` hook.

```javascript
const Profile = () => {
  const { auth, login } = useAuth({ token: null, isLoggedIn: false });

  if (!auth.isLoggedIn)
    return <button onClick={() => login("pass")}>Login</button>;

  return <p>Welcome, you are logged in.</p>;
};

const Settings = () => {
  const { auth, login } = useAuth({ token: null, isLoggedIn: false });

  if (!auth.isLoggedIn)
    return <button onClick={() => login("pass")}>Login</button>;

  return (
    <div>
      <form>
        <label>Change your password</label>
        <input type="password" placeholder="New password" />
        <button>Save</button>
      </form>
    </div>
  );
};
```

The `useAuth` hook may handle multiple scenarios and be used interchangeably in other components by giving the initial auth state as a parameter. This enables the components to have varied expectations for the hook while still functioning appropriately, in accordance with the Liskov Substitution Principle.