Rules Hub
Coding Rules Library
Extract side effects and data fetching into custom hooks
Move data fetching, subscriptions, and complex state logic into custom hooks to keep components focused on presentation.
Bad example
| 1 | export function UserProfile({ id }: { id: string }) { |
| 2 | const [user, setUser] = useState<User | null>(null); |
| 3 | const [loading, setLoading] = useState(true); |
| 4 | const [error, setError] = useState(false); |
| 5 |
|
| 6 | useEffect(() => { |
| 7 | const controller = new AbortController(); |
| 8 | fetch(`/api/users/${id}`, { signal: controller.signal }) |
| 9 | .then((res) => res.json()) |
| 10 | .then((data) => { |
| 11 | setUser(data); |
| 12 | setLoading(false); |
| 13 | }) |
| 14 | .catch((err) => { |
| 15 | if (err.name !== 'AbortError') setError(true); |
| 16 | }); |
| 17 |
|
| 18 | return () => controller.abort(); |
| 19 | }, [id]); |
| 20 |
|
| 21 | if (loading) return <Spinner />; |
| 22 | if (error) return <Error />; |
| 23 | return <div>{user?.name}</div>; |
| 24 | } |
Explanation (EN)
The component mixes imperative data fetching logic, state management, and error handling with declarative UI rendering. This makes it harder to read, test, and reuse the fetching logic elsewhere.
Objašnjenje (HR)
Komponenta miješa imperativnu logiku dohvaćanja podataka, upravljanje stanjem i rukovanje pogreškama s deklarativnim renderiranjem UI-a. To otežava čitanje, testiranje i ponovnu uporabu logike dohvaćanja na drugim mjestima.
Good example
| 1 | // hooks/useUser.ts |
| 2 | export function useUser(id: string) { |
| 3 | const [user, setUser] = useState<User | null>(null); |
| 4 | const [loading, setLoading] = useState(true); |
| 5 | const [error, setError] = useState(false); |
| 6 |
|
| 7 | useEffect(() => { |
| 8 | // ... fetching logic implementation ... |
| 9 | }, [id]); |
| 10 |
|
| 11 | return { user, loading, error }; |
| 12 | } |
| 13 |
|
| 14 | // components/UserProfile.tsx |
| 15 | export function UserProfile({ id }: { id: string }) { |
| 16 | const { user, loading, error } = useUser(id); |
| 17 |
|
| 18 | if (loading) return <Spinner />; |
| 19 | if (error) return <Error />; |
| 20 | return <div>{user?.name}</div>; |
| 21 | } |
Explanation (EN)
The logic is extracted into a custom hook `useUser`, leaving the component purely focused on presentation based on the hook's state. The logic is now reusable and independently testable.
Objašnjenje (HR)
Logika je izdvojena u prilagođeni hook `useUser`, ostavljajući komponentu isključivo fokusiranu na prezentaciju na temelju stanja hooka. Logika je sada ponovno iskoristiva i može se neovisno testirati.