
I am completely new to React and React hooks; everything honestly feels abstract. But I was able to do some research, and now I can better understand useState, useEffect, useCallback, and the rest.
Hooks are just tools to manage state, side effects, and performance in functional components.
I will break them down the way I wish someone had done for me
useState: storing things that can change
This one is very important.
If your UI needs to change based on something, you need state.
const [count, setCount] = useState(0);
Think of it like this:
countβ the current valuesetCountβ how you update it
When you call setCount, React re-renders the component.
setCount(count + 1);
Understand that:
π‘If something affects what the user sees, it belongs in state.
useEffect: doing things outside rendering
You fetch data, listen to events, update the DOM, that is where useEffect comes in.
useEffect(() => {
console.log("Component mounted");
}, []);
That empty array means:
Run this once when the component loads.
Now with dependencies:
useEffect(() => {
console.log("Count changed:", count);
}, [count]);
Understand that:
No array β runs every render, which is usually bad
Empty array β run once
With dependencies β run when they change
useRef: storing values without re-rendering
Sometimes you want to store something, but you do not want React to re-render when it changes.
That is useRef.
const inputRef = useRef(null);
You can use it for:
Accessing DOM elements
Storing mutable values like timers, previous values, etc
inputRef.current.focus();
useRef persists values across renders without triggering a re-render.useMemo: avoiding unnecessary recalculations
If something is expensive to compute, do not recompute it every render.
const expensiveValue = useMemo(() => {
return heavyCalculation(data);
}, [data]);
React will only recompute when data changes, but it is advised not to use useMemo everywhere, especially if the calculation is cheap, you would just be adding complexity for nothing.
Use it when:
The computation is expensive
Or you are passing values to memoised components.
useCallback: stabilising functions
This one took me a while to understand.
Every time a component renders, functions inside it are recreated; this can cause unnecessary re-renders in child components.
const handleClick = useCallback(() => {
console.log("Clicked");
}, []);
Now handleClick keeps the same reference between renders.
Think of it as:
useCallback=useMemo, but for functions
Again, it is important not to overuse it.
Use it when:
You are passing functions to child components
And those children rely on reference equality (like
React.memo)
useContext: avoiding prop drilling
Passing props down multiple layers is painful.
useContext fixes that.
const ThemeContext = createContext();
const value = useContext(ThemeContext);
Instead of:
<App β Parent β Child β Button>
You can access the value directly where you need it, but do not abuse it.
Global state everywhere quickly becomes messy.
Use it for:
Themes
Authentication
Global settings
Not for every single piece of state.
The Mistake Most People Make
People try to use all hooks everywhere, which is wrong.
Hooks exist so we can use the right tool when needed.
useStateβ when UI changesuseEffectβ when side effects happenuseRefβ when you need persistence without re-renderuseMemoβ when computation is expensiveuseCallbackβ when function identity mattersuseContextβ when props become a mess
Understanding why each exists will make writing React easier.





