![]()
In React, hooks are a powerful feature that allows you to add state and side-effect logic to functional components. While React provides several built-in hooks such as useState, useEffect, useContext, etc., you can also create your own custom hooks to encapsulate and reuse logic across different components.
Custom hooks are essentially JavaScript functions that start with the prefix use and can use any of the built-in React hooks inside them. This allows you to create reusable and composable logic that can be shared across your React components.
1. Why Create Custom Hooks?
- Reusability: Custom hooks enable you to reuse logic across multiple components without duplicating code.
- Separation of Concerns: Custom hooks allow you to separate logic from the component’s rendering logic, making the component code cleaner and easier to maintain.
- Encapsulation: You can encapsulate complex logic in custom hooks, making it easier to manage, test, and debug.
2. Basic Structure of a Custom Hook
A custom hook is a JavaScript function that:
- Starts with
use(e.g.,useFetch,useLocalStorage,useCounter, etc.). - Can use React’s built-in hooks (
useState,useEffect, etc.). - Returns values or functions that can be used by components.
Syntax:
function useCustomHook() {
// Hook logic (e.g., state, side effects)
return [state, actions]; // Return state or functions
}
3. Example 1: Custom Hook for Fetching Data
Here’s an example of a custom hook that fetches data from an API:
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);
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
Explanation:
useFetchis a custom hook that takes aurlas a parameter.- The hook uses
useStateto store the fetcheddata,loadingstate, and anyerrorthat occurs during the fetch. - The
useEffecthook is used to perform the fetch operation when theurlchanges. - The hook returns an object with
data,loading, anderror, which can be used by the component that calls this custom hook.
Usage:
import React from 'react';
import useFetch from './useFetch';
const App = () => {
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 (
<div>
<h1>Posts</h1>
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
};
export default App;
Explanation:
- The
useFetchhook is called in theAppcomponent to fetch posts from the API. - The
data,loading, anderrorstates returned from the hook are used to render different parts of the UI.
4. Example 2: Custom Hook for Managing Local Storage
Here’s a custom hook that helps manage values in localStorage:
import { useState } from 'react';
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
const setValue = (value) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
}
export default useLocalStorage;
Explanation:
useLocalStorageis a custom hook that allows you to read from and write tolocalStorage.- It takes two arguments:
key(thelocalStoragekey) andinitialValue(the initial value to be stored if thelocalStorageitem doesn’t exist). - The hook uses
useStatewith a lazy initializer to retrieve the value fromlocalStorageon the initial render. - The
setValuefunction updates the value both in the state and inlocalStorage.
Usage:
import React from 'react';
import useLocalStorage from './useLocalStorage';
const App = () => {
const [name, setName] = useLocalStorage('name', 'John Doe');
const handleChange = (e) => {
setName(e.target.value);
};
return (
<div>
<h1>Your Name</h1>
<input type="text" value={name} onChange={handleChange} />
<p>Stored name: {name}</p>
</div>
);
};
export default App;
Explanation:
- The
useLocalStoragehook is used to store and retrieve thenamevalue fromlocalStorage. - When the user types in the input field, the
nameis updated in both the component’s state and inlocalStorage.
5. Example 3: Custom Hook for Counter Logic
Here’s a simple counter logic using a custom hook:
import { useState } from 'react';
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(initialValue);
return { count, increment, decrement, reset };
}
export default useCounter;
Explanation:
useCounteris a custom hook for managing a counter state.- It provides functions to
increment,decrement, andresetthe counter value.
Usage:
import React from 'react';
import useCounter from './useCounter';
const CounterApp = () => {
const { count, increment, decrement, reset } = useCounter();
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
};
export default CounterApp;
Explanation:
- The
useCounterhook is used to manage and control the counter state. It returns thecountvalue and functions to update it.
6. Best Practices for Custom Hooks
- Keep It Simple: Custom hooks should encapsulate a single piece of reusable logic. Try to avoid making them too complex or containing unrelated functionality.
- Use Built-in Hooks: You can use any of React’s built-in hooks inside your custom hooks (
useState,useEffect,useContext, etc.) to build more complex logic. - Return Values: Custom hooks typically return an array or an object. Return only what the consuming component needs.
- Encapsulate Complex Logic: Custom hooks are perfect for hiding complexity (e.g., managing forms, fetching data, dealing with timers, etc.).
