Rules Hub

Coding Rules Library

← Back to all rules
frontend ruleStack: react
reacthooksclean-codeseparation-of-concernsdata-fetching

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.

PR: Feat/FCK-1623 - Adding eAvis widget to the sidebar #3643Created: Dec 8, 2025

Bad example

Old codetsx
1export 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

New codetsx
1// hooks/useUser.ts
2export 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
15export 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.