Custom hooks are a powerful feature of React that allows you to encapsulate and reuse logic across multiple components. Whether you’re managing form input, fetching data, or handling stateful logic, custom hooks help keep your components clean and modular.
In this module, we’ll learn how to create custom hooks, understand their purpose, and explore some practical examples of how to encapsulate logic in a reusable way.
Table of Contents
- What Are Custom Hooks?
- When to Use Custom Hooks
- Basic Structure of a Custom Hook
- Creating a Custom Hook for Fetching Data
- Using
useState
anduseEffect
Inside Custom Hooks - Reusing Custom Hooks Across Components
- Best Practices for Writing Custom Hooks
- Code Example: Custom Hook for Form Handling
- Code Example: Custom Hook for Window Resize
- Conclusion
1. What Are Custom Hooks?
Custom hooks are JavaScript functions that allow you to reuse stateful logic across components without changing the component structure. They’re called “custom” because they are built by you to handle specific logic, such as form handling or fetching data from an API.
The key difference between a custom hook and a normal function is that a custom hook can use React hooks like useState
, useEffect
, useContext
, and more. Custom hooks follow a simple naming convention, typically starting with the word use
.
2. When to Use Custom Hooks
You should consider using custom hooks when:
- You need to reuse logic across multiple components.
- You want to keep components clean and modular.
- You need to manage complex state or side effects that could clutter component code.
For example, instead of placing a data-fetching logic in multiple components, you can create a custom hook and use it wherever necessary.
3. Basic Structure of a Custom Hook
A custom hook is essentially a function that uses React hooks internally. It can return values, functions, or both, to be used in components.
Here’s the simplest form of a custom hook:
jsxCopyEditimport { useState } from 'react';
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(prevCount => prevCount + 1);
const decrement = () => setCount(prevCount => prevCount - 1);
return { count, increment, decrement };
}
Usage in a component:
jsxCopyEditfunction Counter() {
const { count, increment, decrement } = useCounter();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
4. Creating a Custom Hook for Fetching Data
One common use case for custom hooks is data fetching. Instead of repeating the same useEffect
logic in multiple components, you can abstract it into a custom hook.
Here’s how you can create a useFetch
hook:
jsxCopyEditimport { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
Usage in a component:
jsxCopyEditfunction Posts() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts');
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
5. Using useState
and useEffect
Inside Custom Hooks
Inside custom hooks, you can use any React hook. This allows you to manage state and side effects as needed. For example, using useState
and useEffect
together can allow you to handle form state and form validation in a custom hook.
6. Reusing Custom Hooks Across Components
Once a custom hook is created, it can be reused in any functional component. This is particularly helpful when you need to apply the same logic in multiple places without repeating code.
For example, if you have a useCounter
hook, it can be used in multiple components that require a counter without redefining the logic.
7. Best Practices for Writing Custom Hooks
- Descriptive Naming: Start the hook name with
use
, e.g.,useFetch
,useCounter
,useLocalStorage
. - Keep Hooks Simple: Each custom hook should focus on a specific concern or task.
- Return Useful Values: Return state values and any functions that help control the hook’s behavior.
- Avoid Side Effects in Custom Hooks: Custom hooks should not produce side effects like data fetching directly inside components. If side effects are needed, use
useEffect
within the custom hook.
8. Code Example: Custom Hook for Form Handling
jsxCopyEditimport { useState } from 'react';
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const handleChange = (e) => {
const { name, value } = e.target;
setValues(prevValues => ({
...prevValues,
[name]: value
}));
};
const resetForm = () => setValues(initialValues);
return { values, handleChange, resetForm };
}
Usage:
jsxCopyEditfunction Form() {
const { values, handleChange, resetForm } = useForm({ username: '', email: '' });
return (
<form>
<input
type="text"
name="username"
value={values.username}
onChange={handleChange}
/>
<input
type="email"
name="email"
value={values.email}
onChange={handleChange}
/>
<button type="button" onClick={resetForm}>Reset</button>
</form>
);
}
9. Code Example: Custom Hook for Window Resize
jsxCopyEditimport { useState, useEffect } from 'react';
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
const handleResize = () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowSize;
}
Usage:
jsxCopyEditfunction App() {
const { width, height } = useWindowSize();
return (
<div>
<p>Window Width: {width}</p>
<p>Window Height: {height}</p>
</div>
);
}
10. Conclusion
Custom hooks are an incredibly powerful feature in React, allowing you to encapsulate logic and reuse functionality across different components. By using useState
, useEffect
, and other React hooks inside your custom hooks, you can create cleaner and more maintainable code, reducing duplication and improving the scalability of your application.