In outer space, resources are limited and must be used wisely. Similarly in the React world, application performance is of key importance. Memoization is an optimization technique that allows "remembering" results of expensive operations and reusing them when input data hasn't changed - similar to saving and reusing trajectory calculations instead of performing them from scratch.
Memoization in React is a performance optimization mechanism that allows you to:
`React.memo` is a higher-order component (HOC) that wraps a component and prevents it from re-rendering if its props haven't changed. zmianie.
1import React from 'react';
2
3// Component without memoization
4function PlanetInfo({ name, diameter, distance }) {
5 console.log(\`Rendering planet info: \${name}\`);
6 return (
7 <div className="planet-info">
8 <h2>{name}</h2>
9 <p>Diameter: {diameter} km</p>
10 <p>Distance from Sun: {distance} million km</p>
11 </div>
12 );
13}
14
15// Same component with memoization
16const MemoizedPlanetInfo = React.memo(PlanetInfo);
17
18// Parent component
19function SolarSystem() {
20 const [count, setCount] = useState(0);
21
22 return (
23 <div>
24 <button onClick={() => setCount(count + 1)}>
25 Counter: {count}
26 </button>
27
28 {/* This component will only re-render when its props change */}
29 <MemoizedPlanetInfo
30 name="Mars"
31 diameter={6779}
32 distance={228}
33 />
34 </div>
35 );
36}Notice that in the above example, even though the parent component `SolarSystem` renders again on every button click, the component `MemoizedPlanetInfo` will not re-render because its props remain unchanged.
By default `React.memo` compares props using shallow comparison. However, you can provide your own comparison function:
1const MemoizedPlanetInfo = React.memo(
2 PlanetInfo,
3 (prevProps, nextProps) => {
4 // Return true if you want to prevent re-rendering
5 // Return false if you want to re-render the component
6 return prevProps.name === nextProps.name &&
7 prevProps.diameter === nextProps.diameter;
8 // Note that we ignore changes to distance - the component won't re-render
9 // when only this property changes
10 }
11);Hook `useMemo` allows remembering the result of an expensive operation and reusing it as long as dependencies haven't changed.
1import React, { useState, useMemo } from 'react';
2
3function AsteroidCalculator({ asteroids }) {
4 const [filter, setFilter] = useState('all');
5
6 // Expensive calculation - memoized for better performance
7 const dangerousAsteroids = useMemo(() => {
8 console.log('Calculating dangerous asteroids...');
9 return asteroids.filter(asteroid => {
10 // Simulation of expensive calculations
11 let isDangerous = false;
12 for (let i = 0; i < 10000; i++) {
13 if (asteroid.velocity > 20 && asteroid.size > 1000 && asteroid.distance < 50000000) {
14 isDangerous = true;
15 }
16 }
17 return isDangerous;
18 });
19 }, [asteroids]); // Calculation will be re-executed only when the asteroids array changes
20
21 // This calculation is also memoized but depends on the filter
22 const displayedAsteroids = useMemo(() => {
23 console.log('Filtering asteroids...');
24 if (filter === 'all') return asteroids;
25 if (filter === 'dangerous') return dangerousAsteroids;
26 return [];
27 }, [filter, asteroids, dangerousAsteroids]);
28
29 return (
30 <div>
31 <div>
32 <button onClick={() => setFilter('all')}>All</button>
33 <button onClick={() => setFilter('dangerous')}>Nobezpieczne</button>
34 </div>
35 <h2>Asteroids ({displayedAsteroids.length}):</h2>
36 <ul>
37 {displayedAsteroids.map(asteroid => (
38 <li key={asteroid.id}>{asteroid.name}</li>
39 ))}
40 </ul>
41 </div>
42 );
43}Hook `useCallback` is similar to `useMemo`, but instead of remembering the function's result, it remembers the function itself. It's particularly useful when passing functions as props to memoized child components.
1import React, { useState, useCallback } from 'react';
2
3function MissionControl() {
4 const [missionStatus, setMissionStatus] = useState('preparing');
5 const [fuelLevel, setFuelLevel] = useState(100);
6
7 // Z useCallback, function is recreated only when fuelLevel changes
8 const startMission = useCallback(() => {
9 if (fuelLevel > 20) {
10 setMissionStatus('in-progress');
11 console.log(\`Mission started with fuel level: \${fuelLevel}%\`);
12 } else {
13 console.log('Not enough fuel to start mission');
14 }
15 }, [fuelLevel]); // Dependency: function recreated when fuelLevel changes
16
17 const abortMission = useCallback(() => {
18 setMissionStatus('aborted');
19 console.log('Mission aborted!');
20 }, []); // Empty dependency array - function created only once
21
22 return (
23 <div>
24 <h2>Mission status: {missionStatus}</h2>
25 <p>Fuel level: {fuelLevel}%</p>
26
27 <button onClick={() => setFuelLevel(prev => Math.max(0, prev - 10))}>
28 Use fuel
29 </button>
30
31 {/* Passing memoized functions as props */}
32 <MemoizedControlPanel
33 onStart={startMission}
34 onAbort={abortMission}
35 fuelLevel={fuelLevel}
36 />
37 </div>
38 );
39}
40
41// Memoized child component that receives functions as props
42const ControlPanel = React.memo(({ onStart, onAbort, fuelLevel }) => {
43 console.log('Rendering control panel');
44
45 return (
46 <div className="control-panel">
47 <h3>Control panel</h3>
48 <button onClick={onStart} disabled={fuelLevel <= 20}>
49 Start
50 </button>
51 <button onClick={onAbort}>
52 Abort mission
53 </button>
54 </div>
55 );
56});
57
58const MemoizedControlPanel = React.memo(ControlPanel);| Technique | Use case | When to use | |----------|--------------|--------------| | React.memo | Component | When a component frequently receives the same props | | useMemo | Value | For expensive calculations and complex data structures | | useCallback | Function | When passing functions as props to memoized components |
1// Not optimal - too simple a value to memoize
2const count = useMemo(() => 1 + 2, []);
3
4// Not optimal - simple function that doesn't need memoization
5const handleClick = useCallback(() => {
6 console.log('Clicked!');
7}, []);1// Not safe - missing dependency 'value'
2const processedData = useMemo(() => {
3 return heavyProcessing(value);
4}, []); // Should be [value]1// May reduce memoization effectiveness
2const result = useMemo(() => {
3 return computeResult(a, b);
4}, [a, b, c, d, e]); // Too many dependencies - memoization may be ineffectiveMemoization in React is a powerful optimization technique that - like conserving resources on a spaceship - helps efficiently manage application performance. Use it wisely:
However, remember that like any optimization technique, memoization has its cost and should be applied judiciously, where it actually brings performance benefits.