DEV Community

Cover image for Single Responsibility Principle in React
Mikhael Esa
Mikhael Esa

Posted on • Updated on

Single Responsibility Principle in React

S (Single Responsibility Principle)

A class should have only one reason to change.

Too much responsibility

The Single Responsibility Principle (SRP) in React suggests that each component should have one clear responsibility or role. In other words, a React component should do one thing and do it well.

This principle helps keep your codebase clean and maintainable by ensuring that each component is focused on a specific task. For example, if you have a button component, its responsibility should be limited to rendering the button and handling user interactions. Any unrelated tasks, such as data fetching or state management, should be handled by separate components or modules.

By adhering to the SRP, your React code becomes more organized, easier to understand, and less prone to errors, making it more efficient to develop and maintain.

Let's take a look at this sample code.

const BookList = () => {
  const [books, setBooks] = useState<IBook[]>([]);

  const fetchBooks = async () => {
    try {
      const response = await fetch("https://api.com/books");
      const data = await JSON.parse(response);
      setBooks(data);
    } catch (err: any) {
      console.log(err.message);
    }
  };

  useEffect(() => {
    fetchBooks();
  }, []);

  return (
    <div>
      {books?.map((book) => (
        <Book title={book.title} image={book.image} key={book.id} />
      ))}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

The code seems pretty standard here. What it does is it fetch book data and render it.

Apparently the code violates this principle because the code would have more than one reason to change or more than a single responsibility which are rendering and fetching.

What we can do about it is to simply separate the logic between the rendering and fetching and the component would only receive the final data and doesn't care about the fetching logic.

  // useGetBooks.hook.tsx
const useGetBooks = () => {
  // We moved the fetching logic into a custom hook
  const [books, setBooks] = useState<IBook[]>([]);

  const fetchBooks = async () => {
    try {
      const response = await fetch("https://api.com/books");
      const data = await JSON.parse(response);
      setBooks(data);
    } catch (err: any) {
      console.log(err.message);
    }
  };

  useEffect(() => {
    fetchBooks();
  }, []);

  // Return only the final books result
  return { books }
}
Enter fullscreen mode Exit fullscreen mode
// BookList.component.tsx
const BookList = () => {
  // We call the hook and retrieve the books
  const { books } = useGetBooks();

  return (
    <div>
      {books?.map((book) => (
        <Book title={book.title} image={book.image} key={book.id} />
      ))}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Now we have two different files which has their own responsibility and it looks neat!

What's next?

Let's not stop here since there are 4 more principles left to learn in SOLID which I will cover in the next article so stay tuned.

Dadah~ 👋

Top comments (0)