Chapter 2 of 4
Extracting a Hook
Refactoring a component into a hook, and choosing what to return.
The usual path is: write the logic inside a component first, then move it out when a second component needs it - or when the component gets hard to read.
// Before: fetching logic mixed into the component
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let isCurrent = true;
setIsLoading(true);
fetchUser(userId)
.then((data) => { if (isCurrent) { setUser(data); setError(null); } })
.catch((err) => { if (isCurrent) setError(err); })
.finally(() => { if (isCurrent) setIsLoading(false); });
return () => { isCurrent = false; };
}, [userId]);
if (isLoading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return <h1>{user.name}</h1>;
}// After: the hook owns the mechanics
function useUser(userId) {
const [state, setState] = useState({ user: null, isLoading: true, error: null });
useEffect(() => {
let isCurrent = true;
setState((previous) => ({ ...previous, isLoading: true }));
fetchUser(userId)
.then((user) => isCurrent && setState({ user, isLoading: false, error: null }))
.catch((error) => isCurrent && setState({ user: null, isLoading: false, error }));
return () => { isCurrent = false; };
}, [userId]);
return state;
}
function UserProfile({ userId }) {
const { user, isLoading, error } = useUser(userId);
if (isLoading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return <h1>{user.name}</h1>;
}Return an array or an object?
- Array when there are one or two values the caller will want to rename, like
useState. - Object when there are several, so callers destructure by name and the order does not matter.
const [value, setValue] = useToggle(false); // array: names are chosen by caller
const { user, isLoading, error } = useUser(id); // object: self-documentingKeep hooks focused
A hook that fetches data, tracks scroll position and writes to localStorage is three hooks. Small hooks compose; large ones only get larger.