Beginner's Guide to React Hooks Concepts
Confused by useEffect? We explain state, effects, and refs in simple terms so you can ditch Class components forever.
The Era of Functional Components#
Before 2018, if you wanted "State" (like a counter number) in React, you had to write a Class Component.
It was verbose. It was confusing (this.bind(this) anyone?).
Then came Hooks. Hooks allow you to use State and Lifecycle features inside simple Functions. They changed React forever.
While there are many hooks, 80% of your complex logic will come from just three: useEffect, useMemo, and useCallback.
1. useEffect: The Engine of Side Effects#
This hook tells React to "do something after render".
It replaces componentDidMount, componentDidUpdate, and componentWillUnmount all at once.
Syntax:
useEffect(() => {
// Logic here (Run after render)
console.log("Component painted!");
return () => {
// Cleanup here (Run before unmounting)
console.log("Component is leaving...");
};
}, [dependency]);
The Dependency Array [] is Critical
undefined(no array): Runs after every render. (Dangerous! Can cause loops).[](empty): Runs only once on mount. (Great for API calls).[prop]: Runs wheneverpropchanges value.
Common Mistake:
Passing an object { id: 1 } as a dependency.
In JS, { id: 1 } !== { id: 1 }. They are different references.
This causes the Effect to run infinitely.
2. useMemo: The Performance Cache#
useMemo caches the return value of a heavy calculation.
Imagine you have a list of 10,000 users and you want to filter them by name. Filtering is slow. You don't want to re-filter every time the user clicks a unrelated button (causing a re-render).
// Without useMemo: 'filter' runs on every render
const visibleUsers = users.filter(u => u.active);
// With useMemo: 'filter' only runs if 'users' actually changed
const visibleUsers = useMemo(() => {
return users.filter(u => u.active);
}, [users]);
When to use it?
- Filtering/Sorting large lists (>500 items).
- Cryptographic calculations.
- Complex Chart JS data generation.
When NOT to use it?
- Simple math (
a + b). The overhead of React checking ifachanged is heavier than just adding them!
3. useCallback: The Function Stabilizer#
useCallback caches the function definition itself.
This is mostly used to prevent useEffect from firing too often in child components.
// This function is recreated on every render
const handleClick = () => { ... };
// This function stays the same reference between renders
const handleClick = useCallback(() => { ... }, []);
If you pass a function to a child component that uses React.memo, passing a non-memoized function will break the optimization. The child will "think" it got a new prop and re-render anyway.
Summary Cheat Sheet#
| Hook | Returns | Purpose | Use Case |
|---|---|---|---|
useState | [val, set] | Store data | Counters, Form Inputs |
useEffect | void | Sync with outside | API calls, Document Title |
useMemo | Value | Optimize CPU | Heavy Filtering |
useCallback | Function | Optimize Renders | Stable props for children |
useRef | Object | Persist without render | DOM access, Timers |
WebFiddle