1. Why Optimize React Renders?
By default, when a parent component re-renders, React recursively re-renders all of its child components regardless of whether their props have changed. While React's Virtual DOM diffing is fast, unnecessary re-renders in large lists or complex data tables can cause perceptible UI lag.
2. React.memo: Memoizing Child Components
React.memo is a Higher-Order Component that skips re-rendering a component if its props have not changed (shallow comparison):
import React from 'react';
const QuestionOption = React.memo(function QuestionOption({ text, isSelected, onSelect }) {
console.log("Rendered option:", text);
return (
);
});
export default QuestionOption;
3. useCallback: Memoizing Function References
In JavaScript, functions are objects. Every time a parent component re-renders, it creates a new function instance. Passing that new function down as a prop breaks React.memo! useCallback caches the function instance:
import { useState, useCallback } from 'react';
function QuestionScreen() {
const [selectedId, setSelectedId] = useState(null);
// useCallback caches the callback function across re-renders
const handleSelect = useCallback((id) => {
setSelectedId(id);
}, []);
return handleSelect(1)} />;
}
4. useMemo: Caching Expensive Computations
Use useMemo to cache the return value of an expensive calculation so it only recalculates when dependencies change:
import { useMemo } from 'react';
function AnalyticsLeaderboard({ students }) {
// Only re-calculates average percentile when students array changes
const averageScore = useMemo(() => {
console.log("Computing complex statistics...");
if (!students.length) return 0;
const total = students.reduce((acc, s) => acc + s.score, 0);
return (total / students.length).toFixed(2);
}, [students]);
return Batch Average Score: {averageScore}%;
}