Custom Hooks
In React, when we notice lines of code repeating, it's often a sign that we should refactor them into a function. Similarly, React's standard library doesn't cover every use case, so we can create our own functions, just like we can create our own custom hooks.
What Are Custom Hooks?
Custom hooks are simply JavaScript functions that begin with the prefix use
, and they can call other hooks within them. They allow us to extract reusable logic, particularly when working with state or side effects. By doing so, custom hooks help make components cleaner, more readable, and easier to maintain.
Key Rules for Creating Custom Hooks
Prefix with "use": Always start your custom hook's name with "use" (e.g.,
useFetch
,useForm
).No JSX Return: Custom hooks should not return JSX code. The JSX should remain inside the component, and the hook should return only data or functions to be used within the component.
Use React Hooks Inside Custom Hooks: You can freely use built-in React hooks like
useState
,useEffect
,useContext
, etc., within your custom hooks.
When Should You Create a Custom Hook?
If you find yourself repeatedly using useEffect
or dealing with something external to React's core environment (like APIs, subscriptions, or browser events), it's a good indication that you might need a custom hook. In such cases, consider moving your entire logic into a custom hook and simply call that hook inside your component.
Refactoring Fetch Logic with Custom Hooks in React
When working with multiple components that all need to fetch data, we might end up duplicating the same logic—useState
, useEffect
, fetch
, parsing to JSON, and updating state. Instead of repeating this logic in every component, we can extract it into a reusable custom hook. Let's walk through how to do this.
Initial Setup: Fetching Data Inside a Component
In our initial setup, we fetch data directly inside the component using useEffect
and store the response in the component's state using useState
.
import { useEffect, useState } from "react"
function App() {
const [data, setData] = useState([])
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/todos')
.then((res) => res.json())
.then((data) => setData(data));
}, [])
return (
<div>
{data && data.map(item => {
return <p key = {item.title}>{item.title}</p>;
})}
</div>
)
}
export default App
Here, we're successfully fetching and displaying a list of titles. However, if you need to use this same logic in multiple components, it becomes repetitive.
Refactoring with a Custom Hook
To avoid duplicating the fetch logic across components, we can extract it into a custom hook. This will allow us to reuse the fetch functionality wherever we need.
Here's how we can create a custom hook for fetching data:
Creating the useFetch
Custom Hook
In a new file useFetch.jsx
, we define our custom hook:
import { useState, useEffect } from "react";
export const useFetch = (url) =>{
const [data, setData] = useState([])
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => setData(data));
}, [])
return [data];
}
This hook handles fetching data from a provided URL. It takes the url
as an argument, fetches the data, and returns the response as data
.
Using the Custom Hook in a Component
Now, instead of repeating the fetch logic in every component, we simply call the useFetch
hook:
import { useFetch} from "./useFetch"
function App() {
const [data] = useFetch("https://jsonplaceholder.typicode.com/todos");
return (
<div>
{data && data.map((item) => {
return <p key = {item.id}>{item.title}</p>;
})}
</div>
);
}
export default App
The Error I Encountered While Using Custom Hooks
I ran into an error while trying to implement a custom hook. Here’s what happened:
What I Did
Initially, I had this setup for my custom hook:
const useFetch = () => {
// Hook logic
};
export default useFetch;
In my App.jsx
file, I tried to import the hook like this:
import { useFetch } from './useFetch';
The Problem
This approach didn’t work because I was trying to import useFetch
as a named export (using curly braces), while in reality, it was exported as a default export. Since there wasn’t a named export called useFetch
, I received the following error:
The requested module '/src/useFetch.jsx' does not provide an export named 'useFetch'
What I Learned
Here’s what I discovered about the difference between named exports and default exports in JavaScript modules:
Named Export (
export const useFetch
):A module can have multiple named exports.
You must import them using the exact export name, enclosed in curly braces, like this:
import { useFetch } from './useFetch';
Default Export (
export default useFetch
):A module can have only one default export.
You can import it with any name, without curly braces:
import useFetch from './useFetch';
On Day 10 of my React learning journey, I explored custom hooks and learned how to simplify my code by refactoring repetitive logic. Creating the useFetch
hook helped me learn ways to eliminate redundancy and keep my components clean. This experience reinforced the importance of understanding named and default exports in JavaScript. I'm excited to find out what’s there to learn next!
Subscribe to my newsletter
Read articles from Aaks directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by