Tools: Day 15 of #100DaysOfCode — Performance Optimization in React

Tools: Day 15 of #100DaysOfCode — Performance Optimization in React

Source: Dev.to

⚡ Why Performance Optimization Matters ## Key Optimization Techniques in React ## Memoization ## 1. React.memo(): Stop Unnecessary Re-renders in Child Components ## Without Optimization ## Problem ## With React.memo() ## How It Helps ## 2. useMemo(): Cache Expensive Calculations ## Without Optimization ## Problem ## With useMemo() ## How It Helps ## 3. useCallback(): Prevent Unnecessary Re-renders Caused by New Function References ## Without Optimization ## Problem ## With useCallback() ## How It Helps ## Code Splitting & Lazy Loading = Faster Initial Loads ## Benefits: ## Image Optimization: A Simple Yet Powerful Boost ## Quick Recap: When to Use What ## Final Thoughts Performance optimization is all about making your React apps run faster, smoother, and more responsive. When your UI feels snappy, users stick around longer, and that matters whether you're building a tiny side project or a large-scale production app. At the core of React performance issues lies one simple fact: React re-renders components when data changes — sometimes more often than needed. In small apps this doesn’t hurt much, but as your component tree grows, unnecessary re-renders can slow everything down. Today we’ll explore essential techniques every React developer should know to avoid wasted renders and improve performance. React is designed to update your UI efficiently. But “efficient” doesn’t always mean “optimal.” If components re-render when their data hasn’t actually changed, your app can: Performance optimization ensures your app keeps doing only the work it truly needs to do. Below are the foundational tools and strategies that help eliminate unnecessary calculations and re-renders in React. Memoization means caching values or results so React doesn’t redo work every time a component renders. React offers three main tools for this: Let’s break each one down with examples. React.memo() is a higher-order component that prevents a functional component from re-rendering if its props don’t change. Even though name="John" never changes, ChildComponent re-renders every time the parent component re-renders. Now ChildComponent only re-renders when the name prop actually changes. Whenever your component performs heavy work (sorting, filtering, computing), React will redo that work on every render—even when the inputs haven’t changed. useMemo() solves this by memoizing the computed value. Filtering and sorting run every time the component re-renders, even if products stays the same. The calculation only runs when products changes. On all other renders, React returns the cached result instantly. React creates a new function instance every time a component re-renders. This becomes an issue when passing functions to memoized child components. Even with React.memo(), the child component re-renders because handleClick is a new function reference on every render. handleClick now keeps the same reference, so the child component will not re-render when count updates. Large apps often load more JavaScript than the user needs at first. Code splitting solves this by loading chunks only when required. Images are often the heaviest part of any web page. Optimizing them improves loading performance instantly. Recommended practices: Performance optimization isn’t about micro-optimizing every line of code, it is about understanding where React spends time and reducing unnecessary work. Tools like React.memo(), useMemo(), and useCallback() help keep your components efficient, while code splitting and image optimization speed up load times for real users in the real world. If you build the habit of recognizing re-render patterns early, you’ll naturally write faster, more scalable React apps. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK: function ChildComponent({ name }) { console.log('ChildComponent rendered'); return <div>Hello {name}</div>; } function ParentComponent() { const [count, setCount] = useState(0); return ( <> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <ChildComponent name="John" /> </> ); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: function ChildComponent({ name }) { console.log('ChildComponent rendered'); return <div>Hello {name}</div>; } function ParentComponent() { const [count, setCount] = useState(0); return ( <> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <ChildComponent name="John" /> </> ); } COMMAND_BLOCK: function ChildComponent({ name }) { console.log('ChildComponent rendered'); return <div>Hello {name}</div>; } function ParentComponent() { const [count, setCount] = useState(0); return ( <> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <ChildComponent name="John" /> </> ); } COMMAND_BLOCK: const ChildComponent = React.memo(({ name }) => { console.log('ChildComponent rendered'); return <div>Hello {name}</div>; }); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: const ChildComponent = React.memo(({ name }) => { console.log('ChildComponent rendered'); return <div>Hello {name}</div>; }); COMMAND_BLOCK: const ChildComponent = React.memo(({ name }) => { console.log('ChildComponent rendered'); return <div>Hello {name}</div>; }); COMMAND_BLOCK: function ProductList({ products }) { const expensiveCalculation = products .filter(p => p.price > 100) .sort((a, b) => b.price - a.price); return <div>{expensiveCalculation.map(p => <div>{p.name}</div>)}</div>; } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: function ProductList({ products }) { const expensiveCalculation = products .filter(p => p.price > 100) .sort((a, b) => b.price - a.price); return <div>{expensiveCalculation.map(p => <div>{p.name}</div>)}</div>; } COMMAND_BLOCK: function ProductList({ products }) { const expensiveCalculation = products .filter(p => p.price > 100) .sort((a, b) => b.price - a.price); return <div>{expensiveCalculation.map(p => <div>{p.name}</div>)}</div>; } COMMAND_BLOCK: function ProductList({ products }) { const expensiveCalculation = useMemo(() => { return products .filter(p => p.price > 100) .sort((a, b) => b.price - a.price); }, [products]); return <div>{expensiveCalculation.map(p => <div>{p.name}</div>)}</div>; } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: function ProductList({ products }) { const expensiveCalculation = useMemo(() => { return products .filter(p => p.price > 100) .sort((a, b) => b.price - a.price); }, [products]); return <div>{expensiveCalculation.map(p => <div>{p.name}</div>)}</div>; } COMMAND_BLOCK: function ProductList({ products }) { const expensiveCalculation = useMemo(() => { return products .filter(p => p.price > 100) .sort((a, b) => b.price - a.price); }, [products]); return <div>{expensiveCalculation.map(p => <div>{p.name}</div>)}</div>; } COMMAND_BLOCK: function ParentComponent() { const [count, setCount] = useState(0); const handleClick = () => { console.log('Button clicked'); }; return ( <> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <ChildComponent onClick={handleClick} /> </> ); } const ChildComponent = React.memo(({ onClick }) => { console.log('ChildComponent rendered'); return <button onClick={onClick}>Click me</button>; }); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: function ParentComponent() { const [count, setCount] = useState(0); const handleClick = () => { console.log('Button clicked'); }; return ( <> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <ChildComponent onClick={handleClick} /> </> ); } const ChildComponent = React.memo(({ onClick }) => { console.log('ChildComponent rendered'); return <button onClick={onClick}>Click me</button>; }); COMMAND_BLOCK: function ParentComponent() { const [count, setCount] = useState(0); const handleClick = () => { console.log('Button clicked'); }; return ( <> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <ChildComponent onClick={handleClick} /> </> ); } const ChildComponent = React.memo(({ onClick }) => { console.log('ChildComponent rendered'); return <button onClick={onClick}>Click me</button>; }); COMMAND_BLOCK: function ParentComponent() { const [count, setCount] = useState(0); const handleClick = useCallback(() => { console.log('Button clicked'); }, []); return ( <> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <ChildComponent onClick={handleClick} /> </> ); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: function ParentComponent() { const [count, setCount] = useState(0); const handleClick = useCallback(() => { console.log('Button clicked'); }, []); return ( <> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <ChildComponent onClick={handleClick} /> </> ); } COMMAND_BLOCK: function ParentComponent() { const [count, setCount] = useState(0); const handleClick = useCallback(() => { console.log('Button clicked'); }, []); return ( <> <button onClick={() => setCount(count + 1)}>Count: {count}</button> <ChildComponent onClick={handleClick} /> </> ); } - Become slow on low-end devices - Waste CPU cycles - Render unnecessary DOM updates - React.memo() → Memoizes components - useMemo() → Memoizes expensive calculations - useCallback() → Memoizes function references - React.lazy() → Load components dynamically - <Suspense> → Show fallback UI while loading - Reduced initial bundle size - Faster page load - Faster time-to-interactive - Use modern formats like WebP - Compress images - Lazy load images outside the viewport - Serve responsive images based on device size