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

React Performance Optimization

Performance Techniques

TechniqueWhat it doesWhen to use
React.memo()Skip re-render if props unchangedPure components with expensive renders
useMemo()Cache expensive calculation resultHeavy computations in render
useCallback()Cache function referenceCallbacks passed to memoized children
lazy() + SuspenseCode-split and lazy load componentsLarge components, routes
useTransition()Mark updates as non-urgentSlow renders that shouldn't block UI
Virtual listsRender only visible itemsLists with 1000+ items
React.memo, lazy/Suspense, useTransition, Code Splitting
import { memo, useMemo, useCallback, useState } from 'react'

// React.memo — skip re-render if props haven't changed
const ExpensiveItem = memo(function ExpensiveItem({ item, onDelete }) {
    console.log('Rendering item:', item.id)  // only when item or onDelete changes
    return (
        <div>
            <span>{item.name}</span>
            <button onClick={() => onDelete(item.id)}>Delete</button>
        </div>
    )
})

// Custom comparison function for React.memo
const UserCard = memo(
    function UserCard({ user }) {
        return <div>{user.name}</div>
    },
    (prevProps, nextProps) => {
        // Return true to SKIP re-render (props are equal)
        return prevProps.user.id === nextProps.user.id
            && prevProps.user.name === nextProps.user.name
    }
)

function ItemList() {
    const [items, setItems] = useState([
        { id: 1, name: 'Item 1' },
        { id: 2, name: 'Item 2' },
        { id: 3, name: 'Item 3' },
    ])
    const [count, setCount] = useState(0)

    // useCallback — stable function reference for memoized children
    const handleDelete = useCallback((id) => {
        setItems(prev => prev.filter(item => item.id !== id))
    }, [])  // never changes — no deps

    // useMemo — expensive calculation
    const stats = useMemo(() => {
        console.log('Computing stats...')  // only when items changes
        return {
            total: items.length,
            names: items.map(i => i.name).join(', ')
        }
    }, [items])

    return (
        <div>
            <p>Count: {count} (doesn't re-render items)</p>
            <button onClick={() => setCount(c => c + 1)}>+</button>
            <p>Stats: {stats.names}</p>
            {items.map(item => (
                <ExpensiveItem key={item.id} item={item} onDelete={handleDelete} />
            ))}
        </div>
    )
}
import { lazy, Suspense } from 'react'
import { Routes, Route } from 'react-router-dom'

// lazy() — code split at component level
const HeavyDashboard = lazy(() => import('./HeavyDashboard'))
const AdminPanel     = lazy(() => import('./AdminPanel'))
const Analytics      = lazy(() => import('./Analytics'))

// Loading fallback components
function PageLoader() {
    return (
        <div className="page-loader">
            <div className="spinner"></div>
            <p>Loading...</p>
        </div>
    )
}

function SkeletonCard() {
    return (
        <div className="skeleton">
            <div className="skeleton-title"></div>
            <div className="skeleton-text"></div>
            <div className="skeleton-text short"></div>
        </div>
    )
}

// Route-based code splitting (most common pattern)
function App() {
    return (
        <Suspense fallback={<PageLoader />}>
            <Routes>
                <Route path="/" element={<Home />} />
                <Route path="/dashboard" element={<HeavyDashboard />} />
                <Route path="/admin" element={<AdminPanel />} />
                <Route path="/analytics" element={<Analytics />} />
            </Routes>
        </Suspense>
    )
}

// Component-level lazy loading
function Dashboard() {
    const [showChart, setShowChart] = useState(false)
    const LazyChart = lazy(() => import('./HeavyChart'))

    return (
        <div>
            <button onClick={() => setShowChart(true)}>Load Chart</button>
            {showChart && (
                <Suspense fallback={<SkeletonCard />}>
                    <LazyChart />
                </Suspense>
            )}
        </div>
    )
}
import { useState, useTransition, useDeferredValue } from 'react'

// useTransition — mark state updates as non-urgent
// Keeps UI responsive during slow renders
function SearchPage() {
    const [query, setQuery] = useState('')
    const [results, setResults] = useState([])
    const [isPending, startTransition] = useTransition()

    function handleSearch(e) {
        const value = e.target.value
        setQuery(value)  // urgent — update input immediately

        // Non-urgent — can be interrupted by more urgent updates
        startTransition(() => {
            const filtered = heavyFilter(value)  // slow operation
            setResults(filtered)
        })
    }

    return (
        <div>
            <input value={query} onChange={handleSearch} placeholder="Search..." />
            {isPending && <p>Updating results...</p>}
            <ul style={{ opacity: isPending ? 0.5 : 1 }}>
                {results.map(r => <li key={r.id}>{r.name}</li>)}
            </ul>
        </div>
    )
}

// useDeferredValue — defer a value update
function FilteredList({ query }) {
    // deferredQuery lags behind query — allows urgent renders first
    const deferredQuery = useDeferredValue(query)
    const isStale = query !== deferredQuery

    const filtered = useMemo(() =>
        items.filter(i => i.name.includes(deferredQuery)),
        [deferredQuery]
    )

    return (
        <ul style={{ opacity: isStale ? 0.5 : 1 }}>
            {filtered.map(i => <li key={i.id}>{i.name}</li>)}
        </ul>
    )
}

function heavyFilter(query) {
    // Simulate slow computation
    return Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` }))
        .filter(i => i.name.includes(query))
}

Ready to Level Up Your Skills?

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