Effects, Data Fetching and Custom Hooks
useEffect: syncing with the outside world
Effects run after render, for things outside React: fetching data, subscriptions, timers, browser APIs.
import { useEffect, useState } from "react";
function Repos({ user }) {
const [repos, setRepos] = useState([]);
const [status, setStatus] = useState("loading");
useEffect(() => {
const controller = new AbortController();
setStatus("loading");
fetch(`https://api.github.com/users/${user}/repos`, { signal: controller.signal })
.then(r => { if (!r.ok) throw new Error(r.status); return r.json(); })
.then(data => { setRepos(data); setStatus("done"); })
.catch(err => { if (err.name !== "AbortError") setStatus("error"); });
return () => controller.abort(); // cleanup: cancel if user changes or unmounts
}, [user]); // re-run when user changes
if (status === "loading") return <p>Loading…</p>;
if (status === "error") return <p>Could not load repositories.</p>;
return <ul>{repos.map(r => <li key={r.id}>{r.name}</li>)}</ul>;
}
- The dependency array lists values the effect uses; React re-runs it when they change.
- The cleanup function prevents updates from stale requests and memory leaks.
- Do not use effects to compute values from props or state; calculate them during render.
Custom hooks
Extract reusable logic into functions starting with use:
function useFetch(url) {
const [state, setState] = useState({ data: null, error: null, loading: true });
useEffect(() => {
const c = new AbortController();
fetch(url, { signal: c.signal })
.then(r => r.ok ? r.json() : Promise.reject(r.status))
.then(data => setState({ data, error: null, loading: false }))
.catch(error => error?.name !== "AbortError" && setState({ data: null, error, loading: false }));
return () => c.abort();
}, [url]);
return state;
}
Data libraries
For real apps, use TanStack Query (or SWR): caching, deduplication, background refresh and retries for free.
const { data, isPending, error } = useQuery({
queryKey: ["projects", page],
queryFn: () => fetch(`/api/projects?page=${page}`).then(r => r.json()),
});
Other hooks you will meet
useRef: a mutable value or DOM reference that does not trigger re-renders (focusing inputs).useMemo/useCallback: memoise expensive calculations or callbacks, only when profiling shows a need.useContext: share data (theme, current user) without passing props through every level.
Exercise
Build a GitHub profile viewer with a useFetch hook: input a username, show avatar, bio and top repositories, with loading and error states and request cancellation when the username changes.
Check yourself
- What does the dependency array control?
- Why return a cleanup function from an effect?
- When should you not use an effect?
ההתקדמות שלכם נשמרת בדפדפן הזה בלבד.
שליחה לבדיקה (10 נקודות)