The SRP principle emphasizes the importance of keeping code modular and focused. In the context of React, this translates to ensuring that each component handles only one responsibility, be it rendering UI, fetching data, or managing state.
Table of contents
Open Table of contents
The Bad Example
Let’s examine a bad implementation that violates the SRP principle. Consider a React component responsible for fetching, filtering, and rendering a list of products. The entire logic is crammed into a single component, making it challenging to read, understand, and maintain.
const BadProductList = () => {
// ...fetching products, filtering, and rendering logic
const [products, setProducts] = useState();
const [filterProducts, setFilterProducts] = useState();
const fetchProducts = async () => {
// fetching products
};
useEffect(() => {
fetchProducts();
}, []);
const handleRating = (rate: number) => {
// rating logic
};
const filteredProducts = useMemo(
// filtering logic
);
return (
<div>
{/* ...rendering logic for products */}
</div>
);
};
In this example, the component handles fetching data, filtering products based on ratings, and rendering the UI—all within a single unit. This violates the SRP as the component has more than one responsibility.
The Good Implementation
Now, let’s refactor the code to adhere to the SRP by breaking down the responsibilities into smaller, focused components and hooks. Let’s split it into different components and hooks.
// Product Component
const Product = ({ product }) => {
// ...rendering logic for a single product
return <div>{/* ...rendering logic for a product */}</div>;
};
// RatingFilter Component
const RatingFilter = ({ handleRating }) => {
// ...rendering and filtering logic for ratings
return <div>{/* ...rendering and filtering logic */}</div>;
};
// useProducts Hook
const useProducts = () => {
// ...fetching and managing state logic
const [products, setProducts] = useState([]);
useEffect(() => {
// ...fetching products logic
}, []);
return products;
};
// useRatingFilter Hook
const useRatingFilter = () => {
// ...rating filtering logic
const handleRating = () => {
// ...logic to handle rating changes
};
return { handleRating };
};
// ProductList Component using the above components and hooks
const GoodProductList = () => {
const products = useProducts();
const { handleRating } = useRatingFilter();
return (
<div>
<RatingFilter handleRating={handleRating} />
{products.map(product => (
<Product key={product.id} product={product} />
))}
</div>
);
};
In this improved version, the SRP is adhered to by creating separate components for rendering a single product (Product) and handling rating filtering (RatingFilter). Additionally, the logic for fetching products and managing state is moved to custom hooks (useProducts and useRatingFilter), promoting reusability and maintainability.
Conclusion
Adhering to the Single Responsibility Principle in React results in cleaner, more maintainable code. By breaking down complex components into smaller, focused units, developers can more easily understand, test, and extend their code. Embracing the SRP not only improves code quality but also facilitates collaboration among team members working on different aspects of a project.