Creating Custom Hooks
Creating Custom Hooks
Custom hooks are functions that start with "use" and can call other hooks.
Basic Custom Hook
import { useState, useEffect } from "react";
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
// Usage
function App() {
const [name, setName] = useLocalStorage("name", "");
return (
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter name"
/>
);
}
Data Fetching Hook
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
async function fetchData() {
try {
setLoading(true);
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error("Failed to fetch");
const json = await response.json();
setData(json);
} catch (err) {
if (err.name !== "AbortError") {
setError(err.message);
}
} finally {
setLoading(false);
}
}
fetchData();
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
// Usage
function UserProfile({ userId }) {
const { data: user, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{user.name}</div>;
}
Naming Conventions
Naming Conventions
Rules
- Must start with "use"
- Follow camelCase
- Be descriptive
Good Names
// Good
useLocalStorage
useFetch
useDebounce
useToggle
usePrevious
useWindowSize
useMediaQuery
useForm
// Bad
localStorageHook
fetchData
toggleState
previous
Naming Pattern
// Pattern: use + [Feature]
useAuth()
useTheme()
useCart()
useNotification()
// Pattern: use + [Action]
useToggle()
useDebounce()
useThrottle()
useInterval()
// Pattern: use + [Data]
useLocalStorage()
useSessionStorage()
useCookie()
useQuery()
File Naming
// hooks/
useAuth.js
useLocalStorage.js
useFetch.js
useDebounce.js
Or grouped:
// hooks/
auth/useAuth.js
storage/useLocalStorage.js
api/useFetch.js
Common Patterns
Common Patterns
useToggle
function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
const toggle = useCallback(() => setValue((prev) => !prev), []);
const setTrue = useCallback(() => setValue(true), []);
const setFalse = useCallback(() => setValue(false), []);
return { value, toggle, setTrue, setFalse };
}
// Usage
function Modal() {
const { value: isOpen, toggle, setFalse: close } = useToggle();
return (
<div>
<button onClick={toggle}>Toggle</button>
{isOpen && (
<div className="modal">
<p>Modal content</p>
<button onClick={close}>Close</button>
</div>
)}
</div>
);
}
useDebounce
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Usage
function SearchInput() {
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, 500);
useEffect(() => {
if (debouncedSearch) {
fetchResults(debouncedSearch);
}
}, [debouncedSearch]);
return <input value={search} onChange={(e) => setSearch(e.target.value)} />;
}
useWindowSize
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return size;
}
// Usage
function ResponsiveComponent() {
const { width } = useWindowSize();
return <div>{width < 768 ? <MobileView /> : <DesktopView />}</div>;
}
usePrevious
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
// Usage
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return (
<div>
<p>Now: {count}, Before: {prevCount}</p>
</div>
);
}
useInterval
function useInterval(callback, delay) {
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
const id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
// Usage
function Timer() {
const [count, setCount] = useState(0);
useInterval(() => {
setCount((c) => c + 1);
}, 1000);
}
Practice Problems
Create a reusable React component implementing Custom Hooks. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for Custom Hooks using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize Custom Hooks for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. What must custom hook names start with?
2. Can custom hooks call other hooks?
3. What is the benefit of custom hooks?
4. Where should custom hooks be defined?
Flashcards
Question
What is a custom hook?
Click to reveal answer
Answer
A function that starts with 'use' and can call other hooks
Question
Why use custom hooks?
Click to reveal answer
Answer
To extract and reuse stateful logic
Question
What is the naming convention for custom hooks?
Click to reveal answer
Answer
Start with 'use' and follow camelCase
Question
Can custom hooks return values?
Click to reveal answer
Answer
Yes, they can return any value including arrays, objects, and functions
Question
What is Custom Hooks?
Click to reveal answer
Answer
Custom Hooks is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Custom hooks start with 'use'
- 2.They can call other hooks
- 3.Extract reusable stateful logic
- 4.Follow naming conventions
- 5.Return values and setter functions
Interview Tips
- •Show how to create a custom hook
- •Explain when to extract logic into a hook
- •Demonstrate common custom hook patterns
Cheat Sheet
Cheat Sheet
Custom Hook Structure
function useSomething(args) {
const [state, setState] = useState(initial);
// ... logic
return [state, setState];
}
Common Patterns
useLocalStorage(key, initial)
useFetch(url)
useToggle(initial)
useDebounce(value, delay)
useWindowSize()
usePrevious(value)
useInterval(callback, delay)
Rules
- Start with 'use'
- Can call other hooks
- Return values/functions
- Extract reusable logic