DEV Community

yashi srivastava
yashi srivastava

Posted on

React Hooks & Performance Concepts

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 in setInterval can lead to stale state issues:
  setTime(time + 1) // Might use old `time` value
Enter fullscreen mode Exit fullscreen mode
  • Solution: Use the functional updater to access the latest state:
  setTime(prevTime => prevTime + 1)
Enter fullscreen mode Exit fullscreen mode
  • 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
    
  • 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:

  1. Using let:
  • Value changes in memory but does not trigger UI updates.
  • Resets on re-renders since it’s recreated each time.
  1. Using useState:
  • Changes trigger re-renders, so updated values reflect in the UI.
  1. 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:

  1. Problem:
  • Fibonacci function recalculates on every render, even if only unrelated state changes (count) happen.
  • Causes unnecessary computation and slows performance.
  1. 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 and useMemo 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

  1. For timers or intervals, always store the reference in useRef.
  2. Use useState when UI needs to update, useRef when you want to persist values silently.
  3. Use functional updates with setState to avoid stale closures.
  4. Optimize performance with useCallback (functions) and useMemo (values).
  5. 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)