Optimizing React Performance
/ 3 min read
Table of Contents
Improving React Performance
Performance issues in React apps usually arise from unnecessary re-renders, inefficient state management, and large component trees.
This guide covers:
- Avoiding unnecessary re-renders
- Optimizing state management
- Using memoization and lazy loading
- Implementing virtualization for large lists
Avoiding Unnecessary Re-Renders
Excessive re-renders slow down React applications. Use React.memo, useCallback, and useMemo to optimize renders.
Using React.memo
to Prevent Re-Renders
If a component receives the same props but still re-renders, wrap it with React.memo
.
import React from "react";
type ButtonProps = { text: string; onClick: () => void;};
const Button = React.memo(({ text, onClick }: ButtonProps) => { console.log("Button rendered"); return <button onClick={onClick}>{text}</button>;});
Using useCallback
to Prevent Unnecessary Function Re-Creation
In child components, passing a new function reference on every render causes unnecessary renders.
Wrap event handlers in useCallback
.
import React, { useState, useCallback } from "react";
const Counter = () => { const [count, setCount] = useState(0);
const increment = useCallback(() => setCount((prev) => prev + 1), []);
return <button onClick={increment}>Count: {count}</button>;};
Using useMemo
for Expensive Computations
Use useMemo
to prevent expensive calculations from running on every render.
import React, { useState, useMemo } from "react";
const HeavyComponent = ({ number }: { number: number }) => { const expensiveCalculation = useMemo(() => { console.log("Expensive function running"); return number ** 2; }, [number]);
return <p>Result: {expensiveCalculation}</p>;};
Optimizing State Management
Avoid prop drilling and excessive re-renders by using global state managers or local component state when appropriate.
Using useContext
with useMemo
Avoid unnecessary re-renders when passing context values.
import React, { createContext, useContext, useState, useMemo } from "react";
const ThemeContext = createContext({ theme: "light", toggleTheme: () => {} });
const ThemeProvider = ({ children }: { children: React.ReactNode }) => { const [theme, setTheme] = useState("light");
const value = useMemo(() => ({ theme, toggleTheme: () => setTheme((prev) => (prev === "light" ? "dark" : "light")), }), [theme]);
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;};
const useTheme = () => useContext(ThemeContext);
Virtualizing Large Lists
Rendering a long list slows down performance. Use React Virtualized or react-window
to only render visible elements.
import { FixedSizeList as List } from "react-window";
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => ( <div style={style}>Item {index}</div>);
const VirtualizedList = () => ( <List height={500} itemCount={1000} itemSize={35} width={300}> {Row} </List>);
Lazy Loading Components
Load components only when needed using React.lazy
and Suspense
.
import React, { Suspense, lazy } from "react";
const HeavyComponent = lazy(() => import("./HeavyComponent"));
const App = () => ( <Suspense fallback={<p>Loading...</p>}> <HeavyComponent /> </Suspense>);
Conclusion
- Use React.memo, useCallback, and useMemo to prevent re-renders.
- Optimize state management with
useContext
anduseReducer
. - Use virtualization for large lists to reduce DOM rendering overhead.
- Implement lazy loading to reduce initial bundle size.
Performance tuning in React requires a thoughtful approach to re-renders, state, and data flow. Applying these techniques will make your React app more efficient.