Tutorials Logic, IN +91 8092939553 info@tutorialslogic.com
FAQs Support
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Interview Questions Website Development
Compiler Tutorials

Advanced Hooks — useRef, useContext, useMemo, useCallback

useRef — Mutable References

useRef returns a mutable ref object whose .current property persists across renders. Unlike state, changing a ref does not trigger a re-render. Common uses: accessing DOM elements directly, storing previous values, holding timers.

useRef, useContext, useMemo, useCallback
import { useRef, useState, useEffect } from 'react'

// 1. Access DOM element
function FocusInput() {
    const inputRef = useRef(null)

    const focusInput = () => {
        inputRef.current.focus()  // direct DOM access
    }

    return (
        <div>
            <input ref={inputRef} placeholder="Click button to focus" />
            <button onClick={focusInput}>Focus Input</button>
        </div>
    )
}

// 2. Store previous value
function PreviousValue() {
    const [count, setCount] = useState(0)
    const prevCount = useRef(0)

    useEffect(() => {
        prevCount.current = count  // update after render
    })

    return (
        <div>
            <p>Current: {count}, Previous: {prevCount.current}</p>
            <button onClick={() => setCount(c => c + 1)}>+</button>
        </div>
    )
}

// 3. Store timer ID (doesn't cause re-render)
function Stopwatch() {
    const [time, setTime] = useState(0)
    const [running, setRunning] = useState(false)
    const intervalRef = useRef(null)

    const start = () => {
        setRunning(true)
        intervalRef.current = setInterval(() => setTime(t => t + 1), 1000)
    }

    const stop = () => {
        setRunning(false)
        clearInterval(intervalRef.current)
    }

    return (
        <div>
            <p>{time}s</p>
            <button onClick={running ? stop : start}>{running ? 'Stop' : 'Start'}</button>
        </div>
    )
}
import { createContext, useContext, useState } from 'react'

// 1. Create context
const ThemeContext = createContext('light')
const UserContext = createContext(null)

// 2. Provide context — wrap components that need it
function App() {
    const [theme, setTheme] = useState('light')
    const [user] = useState({ name: 'Alice', role: 'admin' })

    return (
        <ThemeContext.Provider value={{ theme, setTheme }}>
            <UserContext.Provider value={user}>
                <Navbar />
                <Main />
            </UserContext.Provider>
        </ThemeContext.Provider>
    )
}

// 3. Consume context — any descendant can access it
function Navbar() {
    const { theme, setTheme } = useContext(ThemeContext)
    const user = useContext(UserContext)

    return (
        <nav className={`navbar navbar-${theme}`}>
            <span>Hello, {user?.name}</span>
            <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
                Toggle Theme
            </button>
        </nav>
    )
}

// Best practice: create a custom hook for context
function useTheme() {
    const context = useContext(ThemeContext)
    if (!context) throw new Error('useTheme must be used within ThemeProvider')
    return context
}

// Usage: const { theme, setTheme } = useTheme()
import { useState, useMemo, useCallback, memo } from 'react'

// useMemo — memoize expensive calculations
function ExpensiveList({ items, filter }) {
    // Only recalculates when items or filter changes
    const filteredItems = useMemo(() => {
        console.log('Filtering...')  // won't run on every render
        return items.filter(item =>
            item.name.toLowerCase().includes(filter.toLowerCase())
        )
    }, [items, filter])

    return <ul>{filteredItems.map(i => <li key={i.id}>{i.name}</li>)}</ul>
}

// useCallback — memoize functions (prevent child re-renders)
const ChildButton = memo(({ onClick, label }) => {
    console.log(`${label} rendered`)
    return <button onClick={onClick}>{label}</button>
})

function Parent() {
    const [count, setCount] = useState(0)
    const [other, setOther] = useState(0)

    // Without useCallback: new function on every render → ChildButton re-renders
    // With useCallback: same function reference → ChildButton skips re-render
    const handleIncrement = useCallback(() => {
        setCount(c => c + 1)
    }, [])  // no dependencies — function never changes

    return (
        <div>
            <p>Count: {count}, Other: {other}</p>
            <ChildButton onClick={handleIncrement} label="Increment" />
            <button onClick={() => setOther(o => o + 1)}>Other</button>
            {/* ChildButton won't re-render when "other" changes */}
        </div>
    )
}

// When to use useMemo/useCallback:
// ✓ Expensive calculations (sorting, filtering large arrays)
// ✓ Passing callbacks to memoized children (memo())
// ✗ Simple calculations — overhead not worth it
// ✗ Premature optimization — profile first!

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.