Performance is a crucial factor in React application development. In this article, we’ll explore techniques and best practices to optimize performance.
1. React.memo()
React.memo() helps prevent unnecessary re-renders:
import React, { memo } from "react";
const ExpensiveComponent = memo(({ data }) => {
console.log("Component rendered");
return (
<div>
<h2>{data.title}</h2>
<p>{data.description}</p>
</div>
);
});
// Component only re-renders when props change
export default ExpensiveComponent;
2. useMemo Hook
Use useMemo for expensive calculations:
import React, { useMemo } from "react";
function ProductList({ products, searchTerm }) {
const filteredProducts = useMemo(() => {
return products.filter((product) =>
product.name.toLowerCase().includes(searchTerm.toLowerCase()),
);
}, [products, searchTerm]);
const expensiveCalculation = useMemo(() => {
return filteredProducts.reduce((sum, product) => sum + product.price, 0);
}, [filteredProducts]);
return (
<div>
<p>Total Price: {expensiveCalculation}</p>
{filteredProducts.map((product) => (
<div key={product.id}>
{product.name} - ${product.price}
</div>
))}
</div>
);
}
3. useCallback Hook
Optimize event handlers:
import React, { useState, useCallback } from "react";
function TodoList() {
const [todos, setTodos] = useState([]);
const addTodo = useCallback((text) => {
setTodos((prev) => [...prev, { id: Date.now(), text, completed: false }]);
}, []);
const toggleTodo = useCallback((id) => {
setTodos((prev) =>
prev.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo,
),
);
}, []);
return (
<div>
<TodoForm onAdd={addTodo} />
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} onToggle={toggleTodo} />
))}
</div>
);
}
4. Code Splitting
Split code for faster loading:
import React, { Suspense, lazy } from "react";
// Lazy load components
const Dashboard = lazy(() => import("./Dashboard"));
const Profile = lazy(() => import("./Profile"));
function App() {
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Suspense>
</Router>
);
}
5. Virtualization
Use virtualization for long lists:
import { FixedSizeList as List } from "react-window";
const Row = ({ index, style, data }) => (
<div style={style}>
<div>Item {data[index].name}</div>
</div>
);
function VirtualizedList({ items }) {
return (
<List height={400} itemCount={items.length} itemSize={50} itemData={items}>
{Row}
</List>
);
}
6. Image Optimization
Optimize images:
function OptimizedImage({ src, alt, ...props }) {
return (
<img
src={src}
alt={alt}
loading="lazy"
decoding="async"
{...props}
style={{
...props.style,
aspectRatio: "16/9",
objectFit: "cover",
}}
/>
);
}
Best Practices
1. Avoid inline objects and functions
// ❌ Avoid
function Component() {
return (
<Child style={{ marginTop: 10 }} onClick={() => console.log("clicked")} />
);
}
// ✅ Better
const styles = { marginTop: 10 };
const handleClick = () => console.log("clicked");
function Component() {
return <Child style={styles} onClick={handleClick} />;
}
2. Use keys properly
// ❌ Avoid using index
{
items.map((item, index) => <Item key={index} data={item} />);
}
// ✅ Use unique identifier
{
items.map((item) => <Item key={item.id} data={item} />);
}
3. Debouncing for Search
import { useDeferredValue } from "react";
function SearchResults({ query }) {
const deferredQuery = useDeferredValue(query);
const results = useMemo(() => searchAPI(deferredQuery), [deferredQuery]);
return <ResultsList results={results} />;
}
Conclusion
Optimizing React performance requires:
- Understanding lifecycle and rendering process
- Using React hooks correctly
- Applying appropriate optimization techniques
- Regular performance monitoring and measuring
Remember that “premature optimization is the root of all evil” - only optimize when truly necessary! 🚀