Custom Hooks
Introduction
In React, custom hooks allow you to extract and reuse component logic in a modular way. They are essentially JavaScript functions that use built-in React hooks (useState
, useEffect
, etc.) to encapsulate behavior, making it reusable across different components. Custom hooks improve code readability, reduce duplication, and provide an efficient way to manage complex logic in functional components.
In this lesson, you will learn:
- What custom hooks are and why they are useful
- How to create custom hooks with examples
- Common use cases for custom hooks in React applications
What Are Custom Hooks?
A custom hook is a function that starts with the word “use” and allows you to reuse stateful logic across different components. Custom hooks can handle various behaviors, such as data fetching, form handling, or complex calculations, and encapsulate these behaviors into a single reusable function.
Benefits of Using Custom Hooks
- Code Reusability: Custom hooks let you avoid duplicating code by extracting and reusing logic across multiple components.
- Separation of Concerns: Custom hooks help keep your component logic organized, separating data management and side effects from the main component structure.
- Readability and Maintainability: By encapsulating complex behaviors, custom hooks make components simpler and easier to maintain.
Creating a Custom Hook
To create a custom hook, define a function that starts with "use" and call any necessary hooks (like useState
or useEffect
) inside this function. Custom hooks can accept parameters and return values, which makes them flexible and easy to adapt.
Basic Structure of a Custom Hook:
function useCustomHook(parameter) {
// Use built-in hooks or custom logic
const [state, setState] = useState(initialValue);
// Logic or effect
useEffect(() => {
// Side effect logic here
}, [parameter]);
// Return values that the component can use
return [state, setState];
}
Example 1: Custom Hook for Data Fetching
Fetching data is a common task in React applications. With a custom hook, you can encapsulate this logic and reuse it wherever you need to fetch data.
Creating the useFetch
Hook
This custom hook fetches data from a given API endpoint and returns the data, loading status, and any errors.
import { 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);
if (!response.ok) throw new Error('Failed to fetch data');
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
In this example:
useFetch
accepts aurl
parameter.- It initializes
data
,loading
, anderror
states. - The
fetchData
function handles the API call, error handling, and loading state.
Using useFetch
in a Component
import React from 'react';
import useFetch from './useFetch';
function DataComponent() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts');
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return <div>{JSON.stringify(data)}</div>;
}
This component uses useFetch
to retrieve data and conditionally renders the loading or error message based on the hook’s state.
Example 2: Custom Hook for Form Handling
Managing form inputs and validation can become repetitive. A custom hook can simplify form state management and handle input changes.
Creating the useForm
Hook
This custom hook manages form state and handles input changes.
import { useState } from 'react';
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const handleChange = (event) => {
const { name, value } = event.target;
setValues({ ...values, [name]: value });
};
return { values, handleChange };
}
export default useForm;
In this example:
useForm
acceptsinitialValues
, an object with default values for form inputs.values
holds the current form state, whilehandleChange
updates the form state based on user input.
Using useForm
in a Component
import React from 'react';
import useForm from './useForm';
function FormComponent() {
const { values, handleChange } = useForm({ username: '', email: '' });
const handleSubmit = (e) => {
e.preventDefault();
console.log('Form submitted:', values);
};
return (
<form onSubmit={handleSubmit}>
<label>
Username:
<input type="text" name="username" value={values.username} onChange={handleChange} />
</label>
<label>
Email:
<input type="email" name="email" value={values.email} onChange={handleChange} />
</label>
<button type="submit">Submit</button>
</form>
);
}
This form component uses useForm
to manage the state of its username
and email
inputs, making the code concise and reusable.
Example 3: Custom Hook for Window Size Detection
Sometimes, you may need to detect changes in the window size for responsive design or other UI adjustments. This custom hook keeps track of the window dimensions.
Creating the useWindowSize
Hook
import { 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); // Cleanup on unmount
}, []);
return windowSize;
}
export default useWindowSize;
In this example:
useWindowSize
trackswidth
andheight
of the window.- It updates the dimensions whenever the window is resized, with an effect that cleans up the event listener on component unmount.
Using useWindowSize
in a Component
import React from 'react';
import useWindowSize from './useWindowSize';
function ResponsiveComponent() {
const { width, height } = useWindowSize();
return (
<div>
<p>Window width: {width}px</p>
<p>Window height: {height}px</p>
</div>
);
}
This component displays the current window dimensions using the useWindowSize
hook, which automatically updates on resize.
Tips for Creating Effective Custom Hooks
- Keep Hooks Focused: Each custom hook should ideally manage one specific responsibility, like data fetching or form handling.
- Use Built-in Hooks: Inside a custom hook, you can use other built-in hooks such as
useState
oruseEffect
to manage state and side effects. - Start with "use": Always start custom hook names with “use” to follow React’s naming convention, which helps React identify them as hooks.
- Return Only Needed Data: Avoid returning unnecessary data or functions from custom hooks. This keeps the hook API simple and prevents unnecessary re-renders.
Conclusion
In this lesson, we covered:
- The purpose and benefits of custom hooks
- How to create and use custom hooks to encapsulate and reuse component logic
- Examples of custom hooks for data fetching, form handling, and window size detection
Custom hooks empower you to extract and share logic across components efficiently, making your code more readable and maintainable. In the next module, we’ll explore React Router to learn how to manage navigation and routing in React applications.