⚡ Why useRef Can Outperform useState in React Native
Most React Native developers reach for useState by default.
Need to store something?
๐ useState
Need to update something?
๐ useState
But here’s the truth ๐
Not everything belongs in state.
And in performance-sensitive React Native apps,
useRef can be significantly faster than useState.
Let’s understand why — without jargon.
๐ง First, Understand What Triggers a Re-render
In React (and React Native):
Any
setStatecall schedules a re-render
That’s not a bug.
That’s the core design.
This causes:
-
React schedules a re-render
-
Component function runs again
-
Hooks are re-evaluated
-
Virtual tree is re-created
-
Diffing happens
-
Updates are committed to native views
Even if nothing visible changes, the work still happens.
๐งต What Threads Are Involved?
Let’s simplify React Native’s threading model:
๐จ JS Thread
-
Runs your component code
-
Executes hooks (
useState,useRef) -
Handles rendering logic
-
Schedules updates
๐ฆ Native / UI Thread
-
Draws views
-
Handles gestures
-
Talks directly to iOS / Android OS
๐ If JS thread is busy → UI feels laggy
๐ What Happens When You Use useState
Example:
When you update it:
Internally, React does this:
-
JS thread receives
setState -
Update is queued
-
Component is marked “dirty”
-
Component function re-runs
-
New virtual tree is created
-
Diffing compares old vs new tree
-
Commit phase updates native views (if needed)
⚠️ Even if the value is not used in UI, steps 1–6 still happen.
This is why unnecessary state updates are expensive.
๐ง What Makes useRef Different?
Now compare with:
Update it like this:
What happens internally?
๐ Almost nothing
-
No re-render
-
No virtual tree recreation
-
No diffing
-
No commit phase
-
JS thread continues smoothly
useRef is just a mutable container that React ignores for rendering.
๐ง Simple Mental Model
Think of it like this:
| Hook | React Reacts? | Re-render? |
|---|---|---|
useState | Yes | ✅ Always |
useRef | No | ❌ Never |
๐ฏ Why This Matters More in React Native
In web React:
-
Browser handles layout
-
Re-render cost is often lower
In React Native:
-
Rendering crosses JS ↔ Native boundary
-
JS thread performance is critical
-
Animations, gestures, lists depend on it
Unnecessary re-renders:
-
Block JS thread
-
Delay event handling
-
Cause scroll jank
-
Affect animations
❌ Common useState Misuse
Problem:
-
UI does not depend on
intervalId -
Yet every update triggers a re-render
✅ Correct Approach with useRef
Now:
-
No re-renders
-
No wasted work
-
JS thread stays free
๐งฉ When useRef Is the Better Tool
Use useRef for:
-
Timers (
setTimeout,setInterval) -
Animation values
-
Mutable counters
-
Previous values
-
Flags that don’t affect UI
-
Storing instance-like data
⚠️ Important Rule (Very Important)
❌ Never use useRef for UI data
If the UI depends on it, it must be state.
React will not re-render → UI won’t update.
๐️ Architecture Note (Old vs New)
-
Old architecture: re-renders were more expensive
-
New architecture (Fabric): rendering is faster
-
❗ But unnecessary renders are still unnecessary
No architecture removes the cost of wasted work.
๐ก Final Rule
If changing a value should update the UI →
useState
If changing a value should not update the UI →useRef
Using the right hook is not micro-optimization —
it’s respecting how React Native works internally.





Comments
Post a Comment