Custom Hooks
Overview
As you build React applications, you will find yourself writing the exact same state and useEffect logic across multiple components. For example, fetching data, tracking the mouse position, or listening for window resizing.
Because React Hooks are just JavaScript functions, you can extract this repetitive logic into your own Custom Hooks.
A Custom Hook is simply a standard JavaScript function that starts with the word use (e.g., useWindowSize, useFetch). Inside this function, you can call standard React hooks (like useState and useEffect), encapsulate complex logic, and return whatever data or functions the component needs.
Custom hooks are the ultimate form of logic reusability in React. They allow you to share behavior between components, without sharing the visual UI.
Syntax
// 1. Define the Custom Hook (Must start with 'use')
function useToggle(initialValue = false) {
// Inside the hook, we can use standard React hooks
const [value, setValue] = useState(initialValue);
const toggle = () => {
setValue(prev => !prev);
};
// Return whatever the component might need
return [value, toggle];
}
// 2. Consume the hook in ANY component
function App() {
// Now, toggling a boolean is a clean, 1-line operation!
const [isModalOpen, toggleModal] = useToggle(false);
const [isDarkMode, toggleDarkMode] = useToggle(true);
return (
<div>
<button onClick={toggleDarkMode}>Switch Theme</button>
<button onClick={toggleModal}>Open Modal</button>
</div>
);
}Common Pitfalls
- Not starting the name with 'use': If you name your function
fetchDatainstead ofuseFetchData, React's linter will not recognize it as a hook. It will allow you to call it conditionally, which will break the Rules of Hooks and crash your app. Theuseprefix is strictly mandatory. - Assuming state is shared between instances: If Component A and Component B both call
useToggle(), they do NOT share the same boolean value. Custom hooks reuse the logic, not the state. Every time you call a custom hook, it provisions a completely isolated, independent state instance.
Interview Questions
The primary purpose is logic extraction and reusability. It allows you to take complex, repetitive stateful logic (like fetching data, managing local storage, or handling web sockets) out of the component layer and encapsulate it into a reusable, easily testable function.
No. Unlike useState which must return an array, a custom hook is just a JavaScript function. You can return an array, an object, a primitive string, or absolutely nothing at all. Returning an object { data, isLoading, error } is common for hooks with many properties, while arrays [value, toggle] are common for simple tuples.
Real-World Example
useLocalStorage Hook: This is one of the most famous custom hooks in the React ecosystem. By wrapping useState and localStorage together, we abstract away all the messy JSON.parse and try/catch logic, providing product engineers with a flawless, effortless development experience.
function useLocalStorage(key, initialValue) {
// Initialize state directly from LocalStorage so data persists across refreshes
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
// Create a wrapped setter function that updates State AND LocalStorage simultaneously
const setValue = (value) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.log(error);
}
};
return [storedValue, setValue];
}
// IN THE COMPONENT
function Settings() {
// This behaves exactly like useState, but magically survives page reloads!
const [theme, setTheme] = useLocalStorage('app-theme', 'dark');
}Check Your Knowledge
Test your understanding of Custom Hooks with these quick questions.