1️⃣ Stopwatch Example — Understanding useRef
with setInterval
Scenario:
I built a simple stopwatch using React to understand useRef
and state updates.
Key Concepts:
-
Problem:
Using
setState
directly insetInterval
can lead to stale state issues:
setTime(time + 1) // Might use old `time` value
- Solution: Use the functional updater to access the latest state:
setTime(prevTime => prevTime + 1)
-
Why
useRef
is important:- Stores the interval ID across re-renders (
intervalRef.current
). - Doesn’t trigger re-renders when updated.
- Allows us to clear the interval safely:
clearInterval(intervalRef.current) intervalRef.current = null
- Stores the interval ID across re-renders (
-
Important Takeaways:
-
useRef
= persistent, mutable storage across renders. - Always clear old intervals before starting a new one to prevent multiple intervals running simultaneously.
-
useState
triggers UI updates;useRef
stores values silently.
-
2️⃣ useState
vs let
vs useRef
Scenario:
I experimented with a money
variable using let
, useState
, and useRef
to understand state management.
Key Observations:
-
Using
let
:
- Value changes in memory but does not trigger UI updates.
- Resets on re-renders since it’s recreated each time.
-
Using
useState
:
- Changes trigger re-renders, so updated values reflect in the UI.
-
Using
useRef
:
- Stores a persistent value across renders without triggering UI updates.
- Access using
.current
. - Perfect for storing values like timers, previous state, or DOM references.
Memory Tip:
State = UI updates
Ref = silent memory storage
3️⃣ Performance Optimization — useCallback
and useMemo
Scenario:
Built a Fibonacci calculator to understand when recalculations happen unnecessarily.
Key Observations:
- Problem:
- Fibonacci function recalculates on every render, even if only unrelated state changes (
count
) happen. - Causes unnecessary computation and slows performance.
- Solutions:
-
useCallback
:- Memoizes a function reference.
- Only recreates the function if dependencies change.
- Prevents unnecessary function recreation across re-renders.
-
useMemo
:- Memoizes the result of a calculation.
- Recalculates only if dependencies change (e.g., input value).
- Avoids unnecessary computation when unrelated state changes.
Memory Tip:
- Callback = remembers function
- Memo = remembers result
4️⃣ Important Lessons Learned
- Re-renders: Functions and variables are recreated, but refs remain stable.
-
Intervals & Timers: Always store in
useRef
and clear them to prevent duplicate runs. - State vs Ref: Use state for UI updates; use ref for persistent data without re-renders.
-
Performance Hooks:
useCallback
anduseMemo
prevent unnecessary recalculation and function recreation. -
Debugging Tip: Always check console logs to confirm variable updates that don’t reflect in UI (common with
let
or ref values).
5️⃣ Practical Takeaways for Developers
- For timers or intervals, always store the reference in
useRef
. - Use
useState
when UI needs to update,useRef
when you want to persist values silently. - Use functional updates with
setState
to avoid stale closures. - Optimize performance with
useCallback
(functions) anduseMemo
(values). - Always clear intervals or timeouts to prevent memory leaks and bugs.
6️⃣ Memory Mnemonics
Hook | Purpose | Tip |
---|---|---|
useState |
UI reactive value | State = UI updates |
useRef |
Persistent storage across renders | Ref = silent memory |
useCallback |
Memoize function references | Callback = remembers function |
useMemo |
Memoize computed values | Memo = remembers result |
Top comments (0)