Skip to content

useState vs useRef vs useReducer

  • Purpose : For simple, reactive state (UI should update when the value changes).
  • Re-renders : ✅ Yes (when state updates).
  • When To use : Counters, Form inputs, toggle, etc
  • Eg:
const Counter = () => {
const [count, setCount] = React.useState(0)
return <button onClick={()=> setCount(count++)}>{count}</button>
}

👉 Anytime you want UI to re-render when the value changes → useState.

  • Purpose : For storing mutable value that does not trigger re-render.
  • Re-renders : ❌ No (updating .current won’t re-render).
  • When To use :
    • Accessing DOM element directly.
    • Keeping values across renders(like setTimeout IDs, previous values).
  • Eg:
function Timer (){
const timer = React.useRef(null)
function startTimer (){
timer.current = setInterval(()=>{console.log(tick)}, 1000)
}
function clearTimer(){
clearInterval(timer.current)
}
return(
<>
<button onClick={startTimer}>Start</button>
<button onClick={clearTimer}>Stop</button>
</>
)
}

👉 Use useRef when you want to persist a value but don’t need re-renders.

  • Purpose : For complex state logic (state transitions based on actions).
  • Re-renders : ✅ Yes (when state updates).
  • When To use :
    • When state has multiple sub values.
    • When next state depends on previous state.
    • When using Redux like pattern in single component.
  • Eg:
const Counter = () => {
function reducer (state, action){
switch(action.type){
case 'increment': return {count: state.count + 1}
case 'decrement': return {count: state.count - 1}
}
}
const [state, dispatch] = React.useReducer(reducer, {count: 0})
return (
<>
<p>{state.count}</p>
<button onClick={()=> dispatch({type: 'increment'})}>+</button>
<button onClick={()=> dispatch({type: 'decrement'})}>-</button>
</>
)
}

👉 Use useReducer for state machines or complex logic.

HookRe-renders UI?Best For
useState✅ YesSimple state updates (counter, form field)
useRef❌ NoPersist values without re-render (DOM refs, timers, prev values)
useReducer✅ YesComplex state logic, multiple actions, Redux-like flow

I use useState for simple UI states, useRef when I need to persist values without re-rendering the UI(like timers or DOM refs) and useReducer for complex logic where multiple actions update state in predictable ways.