From the author of the Telegram channel REACT NATIVE HUB
Join a growing community of React Native Devs! 👆
As React Native developers, it’s easy to fall into the trap of building components that do too much — fetching data, managing state, rendering UI, and handling user interactions all in one place. While it might work for small components, this approach quickly becomes a nightmare to maintain and debug as your app scales.
Enter the Single Responsibility Principle (SRP) — a cornerstone of clean code and scalable architecture.
What Is the Single Responsibility Principle?
- Definition: A component should have only one reason to change, meaning it should do one thing and do it well.
- In React Native: Break down large components into smaller, reusable pieces, each handling a single responsibility.
What Happens When You Don’t Follow SRP?
In this example, the UserProfile component handles fetching data, managing state, and rendering the UI — all in one component.
// ❌ BAD EXAMPLE - Violating SRP
import React, { useState, useEffect } from 'react';
const UserProfile = () => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUserData = async () => {
try {
const response = await fetch('<https://api.example.com/user>');
const data = await response.json();
setUser(data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchUserData();
}, []);
if (loading) return <View><Text>Loading...</Text></View>;
if (error) return <View><Text>Error: {error.message}</Text></View>;
return (
<View>
<Text>{user.name}</Text>
<Text>{user.email}</Text>
</View>
);
};
export default UserProfile;
Following SRP
Now, let’s refactor the code to adhere to the Single Responsibility Principle by breaking it into three separate components.
- Custom Hook for Data Fetching (
useUserData
)
// ✅ GOOD EXAMPLE - Following SRP
import { useState, useEffect } from 'react';
const useUserData = () => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUserData = async () => {
try {
const response = await fetch('<https://api.example.com/user>');
const data = await response.json();
setUser(data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchUserData();
}, []);
return { user, loading, error };
};
export default useUserData;
- Loading and Error Handling Component (
UserInfo
)
// ✅ GOOD EXAMPLE - Following SRP
import React from 'react';
const UserInfo = ({ user, loading, error }) => {
if (loading) return <View><Text>Loading...</Text></View>;
if (error) return <View><Text>Error: {error.message}</Text></View>;
return (
<View>
<Text>{user.name}</Text>
<Text>{user.email}</Text>
</View>
);
};
export default UserInfo;
- Main Component (
UserProfile
)
// ✅ GOOD EXAMPLE - Following SRP
import React from 'react';
import useUserData from './useUserData';
import UserInfo from './UserInfo';
const UserProfile = () => {
const { user, loading, error } = useUserData();
return <UserInfo user={user} loading={loading} error={error} />;
};
export default UserProfile;
Why This Matters
By following SRP:
- Your code is easier to test. Each unit can be tested in isolation.
-
You improve reusability. The
UserInfo
component can now be reused anywhere. - Future changes are easier. Want to change how data is fetched? You only touch the hook. Want to redesign the UI? Only modify the UI component.
About me: My name is Arsen, and I am a react native developer and owner of the TG channel 👇
🔗 Join TG community for React Native Devs: REACT NATIVE HUB
Top comments (0)