You can find a summary of the code updates in this pull request . Read on for explanation.
Why custom hooks?
We use custom hooks for the same reasons we use functions – either because
- we want to re-use the same logic over and over, or
- we want to abstract the details of the logic away from where the logic is called.
In this case, we’re creating a custom hook for the second reason. We don’t intend to re-use this logic anywhere else, but it’s nice to have the logic tucked away in its own file. Someone reading the Home
component doesn’t need all those details up front, and the complicated logic gets in the way of understanding how the component works at a high level.
1. Create custom hook file
We’ll create a custom hook file in /src/component/hooks/use-quote-styles.ts. (You’ll need to create the folder as well, since this is our first hook and the hooks folder doesn’t exist yet.)
Within use-quote-styles.ts, we can declare and export the custom hook. We’ll call it useQuoteStyles
, since (all hooks start with use ).
use-quote-styles.ts
function useQuoteStyles() {
}
export default useQuoteStyles;
2. Move the logic
Now we’ll move all the logic (including state) from the Home
component in page.tsx to useQuoteStyles
, and return all state plus the handleClick
function.
A couple notes:
- We’ll need to move the
Status
type over, too, since it’s used in this file now and not page.tsx. - I used the name
fetchQuoteStyles
for thehandleClick
function, becausehandleClick
doesn’t mean as much when it’s not defined right next to the button whose click is being handled.
use-quote-styles.ts
import React from "react";
type Status = "idle" | "loading" | "error";
function useQuoteStyles() {
const [quote, setQuote] = React.useState<string>();
const [status, setStatus] = React.useState<Status>("idle");
const [error, setError] = React.useState<string>();
const fetchQuoteStyles = async () => {
// reset error
setError(undefined);
try {
// start request
setStatus("loading");
const response =
await fetch("/api/get-quote-styles");
if (!response.ok) {
throw new Error(response.statusText);
}
const json = await response.json();
if (!json?.quote) {
throw new Error("Malformed response");
}
setQuote(json.quote);
setStatus("idle");
} catch (error) {
setError(error?.toString());
setStatus("error");
}
};
// return all of the state and `fetchQuoteStyles`
return {
status,
error,
quote,
fetchQuoteStyles
};
}
export default useQuoteStyles;
3. Call the hook
Now we need to call the hook from the Home
component, destructuring the object from the return value. We’ll also need to update the onClick
value for Button
.
page.tsx
// ...
import useQuoteStyles from "@/hooks/use-quote-styles"
// ...
export default function Home() {
const {
status,
error,
quote,
fetchQuoteStyles
} = useQuoteStyles();
return (
<main>
<Button onClick={fetchQuoteStyles}>
use random quote
</Button>
{/* ... */}
</main>
);
}
Up next
Now that the logic is all nicely in place, we’ll update the UI in the next workshop to reflect the loading and error states.