React provides several built-in hooks for working with state, effects, and other features of functional components:
Main hooks:
useState
— for managing local stateuseEffect
— for working with side effectsuseContext
— for using React contextAdditional hooks:
useReducer
— alternative to useState for complex stateuseCallback
— for memoizing functionsuseMemo
— for memoizing valuesuseRef
— for accessing DOM elements and storing mutable valuesuseLayoutEffect
— synchronous alternative to useEffectuseTransition
— for managing update prioritiesuseId
— for generating unique IDsCreating custom hooks — allows extracting reusable logic into separate functions.
React Hooks are functions that allow you to use state and other React features without writing classes. They were introduced in React 16.8 and became a revolution in functional component development. 🚀
The most commonly used hook for managing component local state:
// Simple counter example
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
Used for performing side effects such as API requests, subscriptions, timers:
// Example with subscription and unsubscription
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId]);
return <div>{/* display messages */}</div>;
}
Allows accessing React context values without prop drilling:
// Example using theme
function Button() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background }}>
Button
</button>
);
}
Alternative to useState for complex state update logic:
// Counter example with reducer
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>
Increment
</button>
</div>
);
}
Prevents unnecessary function creation on every render:
// Example optimizing callbacks
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Button clicked');
}, []);
return <Child onClick={handleClick} />;
}
Caches computation results between renders:
// Example of heavy computations
function ExpensiveComponent({ items }) {
const expensiveValue = useMemo(() => {
return items.reduce((acc, item) => acc + item.value, 0);
}, [items]);
return <div>Result: {expensiveValue}</div>;
}
Allows accessing DOM elements or storing mutable values:
// Example focusing on element
function Form() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} />
<button onClick={focusInput}>Focus</button>
</div>
);
}
Analog of useEffect but runs synchronously after DOM changes:
// Example measuring element
function Tooltip() {
const [tooltipHeight, setTooltipHeight] = useState(0);
const tooltipRef = useRef();
useLayoutEffect(() => {
setTooltipHeight(tooltipRef.current.offsetHeight);
});
return (
<div ref={tooltipRef}>
Tooltip height {tooltipHeight}px
</div>
);
}
Allows marking state updates as non-urgent to prevent blocking the interface:
// Example with delayed search
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value);
// Mark results update as non-urgent
startTransition(() => {
// Heavy search operation
const filteredResults = heavySearchOperation(value);
setResults(filteredResults);
});
};
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <div>Loading...</div>}
<ResultsList results={results} />
</div>
);
}
Generates stable unique IDs, useful for accessibility attributes:
// Example creating accessible forms
function FormField({ label, value, onChange }) {
const id = useId();
return (
<div>
<label htmlFor={id}>{label}</label>
<input
id={id}
value={value}
onChange={onChange}
aria-describedby={`${id}-description`}
/>
<p id={`${id}-description`}>
This field is required
</p>
</div>
);
}
Allow extracting reusable logic into reusable functions:
// Example custom hook for data fetching
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return { data, loading };
}
// Using custom hook
function UserProfile({ userId }) {
const { data, loading } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
return <div>Hello, {data.name}!</div>;
}
// ❌ No need to memoize simple values
const value = useMemo(() => 5, []); // Unnecessary!
// ✅ Just use the value directly
const value = 5;
// ❌ Missing dependency
useEffect(() => {
console.log(userId); // userId is used but not in dependencies
}, []);
// ✅ All dependencies specified
useEffect(() => {
console.log(userId);
}, [userId]);
// ❌ Mutating array directly
const [items, setItems] = useState([1, 2, 3]);
items.push(4); // Won't work!
// ✅ Creating new array
const [items, setItems] = useState([1, 2, 3]);
setItems([...items, 4]); // Correct!
// Good: using the right hooks
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <div>Elapsed: {seconds} seconds</div>;
}
// Custom hook for working with local storage
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
const setValue = (value) => {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
};
return [storedValue, setValue];
}
React Hooks are a powerful tool for working with state and other features in functional components:
✅ Main hooks:
useState
— for simple stateuseEffect
— for side effectsuseContext
— for working with context✅ Additional hooks:
useReducer
— for complex stateuseCallback/useMemo
— for optimizationuseRef
— for DOM access and storing valuesuseTransition
— for managing update prioritiesuseId
— for generating unique IDs✅ Custom hooks:
Understanding all hooks and knowing how to apply them correctly is key to effective React development! 🎯
Want more articles to prepare for interviews? Subscribe to EasyAdvice, bookmark the site, and improve yourself every day 💪