🚀 Mastering Hooks in React 19: A Complete Guide from Basics to Advanced with Examples

React 19 Hooks
custom Hook
useState
useEffect
useRef
useCallback
useMemo
useTransition
useDeferredValue
useId
React 19 brings enhancements that improve both performance and developer experience. Hooks are no longer just a functional component feature; they're a fundamental part of building performant, modern applications. This blog covers every aspect of using Hooks in React 19, from simple examples to advanced usage, with production-ready tips and code.
💡 What Are Hooks?
Hooks are special functions in React that allow you to use features like state, context, and side effects in functional components. Before hooks, these features were only available in class components.
React 19 Update: Hooks are now optimized for concurrent rendering, server components, and improved developer ergonomics.
🟢 Basic Hooks in React 19
1. useState()
Used to add local state to functional components.
1import { useState } from 'react';
2
3function Counter() {
4 const [count, setCount] = useState(0);
5 return (
6 <div>
7 <p>Clicked {count} times</p>
8 <button onClick={() => setCount(count + 1)}>Increment</button>
9 </div>
10 );
11}
React 19 Tips:
- Initialize with a function for heavy calculations
- Group related states into objects or arrays for better structure
2. useEffect()
Used to handle side effects like fetching data, subscriptions, etc.
1useEffect(() => {
2 document.title = `Clicked ${count} times`;
3}, [count]);
React 19 Enhancements:
- Better cleanup handling
- Works seamlessly with concurrent features
3. useContext()
Let you consume values from a React context.
1const ThemeContext = React.createContext('light');
2
3function ThemedComponent() {
4 const theme = useContext(ThemeContext);
5 return <div className={theme}>Theme is {theme}</div>;
6}
React 19 Change:
- Concurrent safe
- useContextSelector for more efficient re-renders
🔵 Intermediate Hooks
4. useReducer()
Alternative to useState for complex state logic.
1function reducer(state, action) {
2 switch (action.type) {
3 case 'increment': return { count: state.count + 1 };
4 case 'decrement': return { count: state.count - 1 };
5 default: return state;
6 }
7}
8
9const [state, dispatch] = useReducer(reducer, { count: 0 });
React 19 Feature:
- Supports lazy initialization with init
- Great for server state sync
5. useRef()
Stores a mutable reference that doesn’t trigger re-renders.
1const inputRef = useRef(null);
2<input ref={inputRef} />
Use cases:
- Accessing DOM elements
- Storing interval IDs
- Persisting previous values
6. useMemo() and useCallback()
Used for performance optimization by memoizing values and functions.
1const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
2const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);
React 19 Change:
- Better memoization handling with React.memo and useMemo
💥 Advanced Hooks in React 19
7. useTransition()
Used to mark state updates as non-urgent.
1const [isPending, startTransition] = useTransition();
2
3startTransition(() => {
4 setSearchTerm(input);
5});
Improves performance for heavy UI updates.
8. useDeferredValue()
Defers updating the UI for non-critical state changes.
1const deferredValue = useDeferredValue(value);
9. useId()
Generates unique IDs that are stable across server and client rendering.
1const id = useId();
2return <input id={id} />;
10. useSyncExternalStore()
Standard hook for subscribing to external stores (used in Redux, Zustand).
1const state = useSyncExternalStore(subscribe, getSnapshot);
📈 Creating Custom Hooks
Custom hooks allow you to abstract and reuse stateful logic between components. They must start with the word use and can call other hooks internally.
🔧 Example: useWindowSize
1import { useState, useEffect } from 'react';
2
3function useWindowSize() {
4 const [width, setWidth] = useState(window.innerWidth);
5
6 useEffect(() => {
7 const handleResize = () => setWidth(window.innerWidth);
8 window.addEventListener('resize', handleResize);
9 return () => window.removeEventListener('resize', handleResize);
10 }, []);
11
12 return width;
13}
📦 Example: useLocalStorage
1function useLocalStorage(key, initialValue) {
2 const [storedValue, setStoredValue] = useState(() => {
3 try {
4 const item = window.localStorage.getItem(key);
5 return item ? JSON.parse(item) : initialValue;
6 } catch (error) {
7 return initialValue;
8 }
9 });
10
11 const setValue = (value) => {
12 try {
13 const valueToStore = value instanceof Function ? value(storedValue) : value;
14 setStoredValue(valueToStore);
15 window.localStorage.setItem(key, JSON.stringify(valueToStore));
16 } catch (error) {
17 console.error(error);
18 }
19 };
20
21 return [storedValue, setValue];
22}
🧠 Why Use Custom Hooks?
- Encapsulate logic (e.g., form handling, API fetching)
- Improve code reusability and cleanliness
- Keep components focused on rendering
📖 Best Practices in React 19
- Always call hooks at the top level
- Use TypeScript for hook typing
- Separate concerns with custom hooks
- Use eslint-plugin-react-hooks
- Prefer useTransition For heavy UI changes
- Memoize callbacks passed to children
📅 Real-World Project Example: Debounced Search
1function SearchUsers() {
2 const [query, setQuery] = useState('');
3 const [users, setUsers] = useState([]);
4 const [isPending, startTransition] = useTransition();
5
6 useEffect(() => {
7 const timeout = setTimeout(() => {
8 fetch(`/api/users?q=${query}`)
9 .then(res => res.json())
10 .then(setUsers);
11 }, 300);
12 return () => clearTimeout(timeout);
13 }, [query]);
14
15 const handleChange = (e) => {
16 const value = e.target.value;
17 startTransition(() => {
18 setQuery(value);
19 });
20 };
21
22 return (
23 <div>
24 <input onChange={handleChange} placeholder="Search" />
25 {isPending ? <p>Loading...</p> : users.map(u => <p key={u.id}>{u.name}</p>)}
26 </div>
27 );
28}
🧪 Testing Hooks in React 19
Testing hooks ensures their logic behaves as expected, especially when using custom hooks or managing complex state.
📍 Using @testing-library/react-hooks (or built-in React Testing Library tools)
1import { renderHook, act } from '@testing-library/react-hooks';
2import { useCounter } from './useCounter';
3
4test('should increment counter', () => {
5 const { result } = renderHook(() => useCounter());
6 act(() => result.current.increment());
7 expect(result.current.count).toBe(1);
8});
✅ Best Practices:
- Use act() For state updates
- Prefer testing outcomes over implementation
- Use mock functions for side effects
🧩 Integration with External Libraries
Hooks seamlessly integrate with libraries like Redux, Zustand, or TanStack Query in React 19.
Redux with useSelector and useDispatch:
1const count = useSelector((state) => state.counter.value);
2const dispatch = useDispatch();
Zustand with useStore:
1const count = useStore((state) => state.count);
TanStack Query:
1const { data, isLoading } = useQuery(['users'], fetchUsers);
⚙️ React 19 Edge:
- Works natively with useSyncExternalStore For better concurrent handling
- Encourages modular state management using hooks over HOCs or render props
📚 Resources and Further Reading
- React Docs: Hooks
- React 19 RFCs
- React Testing Library
- Kent C. Dodds: Epic React
- TanStack Query Docs
- Redux Toolkit
🚀 Conclusion
React 19 has matured the hook system into an advanced mechanism for managing state, side effects, and logic abstraction. Mastering both core and advanced hooks unlocks performance gains, readability, and flexibility. To stay ahead in the React ecosystem, continue experimenting with custom hooks and new APIs.