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
setStatedirectly insetIntervalcan 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
useRefis 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.
-
useStatetriggers UI updates;useRefstores 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 updatesRef = 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
useRefand clear them to prevent duplicate runs. - State vs Ref: Use state for UI updates; use ref for persistent data without re-renders.
-
Performance Hooks:
useCallbackanduseMemoprevent unnecessary recalculation and function recreation. -
Debugging Tip: Always check console logs to confirm variable updates that don’t reflect in UI (common with
letor ref values).
5️⃣ Practical Takeaways for Developers
- For timers or intervals, always store the reference in
useRef. - Use
useStatewhen UI needs to update,useRefwhen you want to persist values silently. - Use functional updates with
setStateto 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)