In React, this can be applied like so:
A component should do only one thing
Each custom hook should solve a single problem
A utility function should have just one responsibility
Imagine you need to create a screen that fetches data from an API. This screen needs to:
Fetch the data
Handle the loading state
Render the UI
That means it currently has three different responsibilities, so you can break it down like this:
// use-user.ts export function useUser() {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
fetch('/api/user')
.then((res) => res.json())
.then((data) => {
setUser(data)
setLoading(false)
})
}, [])
return { user, loading }
}
// user-details.tsx type Props = {
user: { name: string; email: string }
}
const UserDetails = ({ user }: Props) => (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
)
useUser
and UserDetails
.That’s why the loading state is handled here—it’s part of the orchestration. The user object is passed as a prop to UserDetails
.
// user-profile.tsx import { useUser } from './use-user' import { UserDetails } from './user-details' function UserProfile() {
const { user, loading } = useUser()
if (loading) return <p>Loading...</p>
return <UserDetails user={user} />
}
More readable code
Easier to test
Easier to reuse
Fewer bugs when making changes
But of course, none of this works if you don’t practice—so go ahead and open up VSCode!