React
Learn React Hooks: A Practical Guide with Examples
Arun Tyagi
September 13, 2025
14 min read
#React#Hooks#useEffect#useState#useMemo#useCallback
What Are Hooks?
Hooks are functions that let you use React features in function components. They replace many class patterns with simpler, reusable logic.
useState
Manage local component state.
"use client";
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
);
}
useEffect
Handle side effects like fetching, subscriptions, and timers.
"use client";
import { useEffect, useState } from 'react';
export default function FetchUser() {
const [user, setUser] = useState(null);
useEffect(() => {
let isMounted = true;
fetch('/api/user').then(r => r.json()).then(data => {
if (isMounted) setUser(data);
});
return () => { isMounted = false };
}, []);
return {JSON.stringify(user, null, 2)}
;
}
useMemo
Memoize expensive calculations.
import { useMemo } from 'react';
function sumLargeArray(values) {
return values.reduce((a, b) => a + b, 0);
}
export default function Stats({ values }) {
const total = useMemo(() => sumLargeArray(values), [values]);
return Total: {total};
}
useCallback
Memoize functions to avoid unnecessary re-renders in children.
import { useCallback, useState } from 'react';
export default function List() {
const [items, setItems] = useState([1,2,3]);
const remove = useCallback((id) => setItems(xs => xs.filter(x => x !== id)), []);
return items.map(id => (
));
}
function Row({ id, onRemove }) {
return
}
useRef
Hold mutable values or access DOM nodes.
import { useRef } from 'react';
export default function FocusInput() {
const inputRef = useRef(null);
return (
<>
>
);
}
useContext
Consume values from React context without prop drilling.
import { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
export default function App() {
return (
);
}
function Toolbar() {
const theme = useContext(ThemeContext);
return Theme: {theme};
}
useReducer
Manage complex state transitions.
import { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'inc':
return { count: state.count + 1 };
case 'dec':
return { count: state.count - 1 };
default:
return state;
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
{state.count}
);
}
useId
Generate stable IDs for accessibility.
import { useId } from 'react';
export default function Field() {
const id = useId();
return (
<>
>
);
}
useLayoutEffect
Like useEffect
but fires synchronously after DOM mutations. Use sparingly.
import { useLayoutEffect, useRef, useState } from 'react';
export default function Measure() {
const ref = useRef(null);
const [w, setW] = useState(0);
useLayoutEffect(() => {
setW(ref.current?.offsetWidth ?? 0);
});
return Width: {w};
}
useImperativeHandle
Customize the instance value exposed by ref
in parent components.
import { forwardRef, useImperativeHandle, useRef } from 'react';
const Input = forwardRef(function Input(_props, ref) {
const innerRef = useRef(null);
useImperativeHandle(ref, () => ({ focus: () => innerRef.current?.focus() }));
return
});
export default function Parent() {
const ref = useRef(null);
return ;
}
useDeferredValue & useTransition
Defer or mark updates as non-urgent to keep the UI responsive.
import { useDeferredValue, useState, useTransition } from 'react';
export default function Search() {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const deferred = useDeferredValue(query);
const results = expensiveFilter(deferred);
function onChange(e) {
const value = e.target.value;
startTransition(() => setQuery(value));
}
return ;
}
Custom Hooks
Extract reusable logic:
import { useEffect, useState } from 'react';
function useWindowWidth() {
const [w, setW] = useState(0);
useEffect(() => {
const onResize = () => setW(window.innerWidth);
onResize();
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);
return w;
}
export default function Component() {
const width = useWindowWidth();
return Width: {width};
}
Best Practices
- Place hooks at the top level, never inside conditions or loops
- Prefer
useReducer
for complex state updates - Memoize props for heavy child components with
useMemo
/useCallback
- Use
useEffect
for effects, not for deriving state from props