These tools are about performance optimization, but they are only useful when you have a real problem to solve.
A good rule is:
- first write clear code
- then optimize only if profiling or a real bottleneck shows you need it
1) memo
memo is a higher-order component that tells React:
"If the props for this component did not change, do not re-render it just because its parent re-rendered."
import { memo } from 'react';
const Counter = memo(function Counter({ initialCount }: { initialCount: number }) {
console.log('Counter rendered');
return <div>Count: {initialCount}</div>;
});
export default function App() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount((prev) => prev + 1)}>Update parent</button>
<Counter initialCount={0} />
</>
);
}
In this example, the parent state changes, but the Counter prop value stays the same. Because the props did not change, React can skip re-rendering the child component.
This is useful when the child is expensive to render and the input props are unchanged.
Important note
Before using memo, check whether the component structure can be improved.
Sometimes the better solution is to move the state lower in the tree or split the component into smaller pieces, instead of adding optimization too early.
2) useCallback
useCallback is used to keep a function reference stable across renders.
This matters when a child component is wrapped in memo and receives a function as a prop.
Without useCallback, the function is recreated every render, so React sees a new prop value and may re-render the child.
import { memo, useCallback, useState } from 'react';
const ButtonRow = memo(function ButtonRow({
onClick,
}: {
onClick: () => void;
}) {
console.log('ButtonRow rendered');
return <button onClick={onClick}>Click me</button>;
});
export default function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
return (
<>
<p>Parent count: {count}</p>
<button onClick={() => setCount((prev) => prev + 1)}>Update parent</button>
<ButtonRow onClick={handleClick} />
</>
);
}
Here, handleClick is stable because it is wrapped in useCallback. Since the function reference does not change, the memoized child does not re-render unnecessarily.
When to use it
Use useCallback when:
- a child is memoized
- a function is passed as a prop
- the function is expensive or created repeatedly
Important detail
State setter functions from useState are already stable and do not need to be wrapped in useCallback.
3) useMemo
useMemo is used to memoize a value, not a component.
A common use case is when an object or array is created during render and then passed as a prop to a memoized child.
If the object is recreated on every render, the child sees a new reference and may re-render even if the contents are conceptually the same.
import { memo, useMemo, useState } from 'react';
const UserCard = memo(function UserCard({
user,
}: {
user: { name: string; age: number };
}) {
console.log('UserCard rendered');
return <div>{user.name} ({user.age})</div>;
});
export default function App() {
const [count, setCount] = useState(0);
const user = useMemo(
() => ({ name: 'Ali', age: 25 }),
[]
);
return (
<>
<button onClick={() => setCount((prev) => prev + 1)}>Update parent</button>
<UserCard user={user} />
</>
);
}
The object user is created once and kept stable by useMemo. This helps prevent unnecessary re-renders of UserCard.
Summary of the pattern
-
memoprevents re-rendering of a component when props do not change -
useCallbackkeeps function references stable -
useMemokeeps object/array references stable
These are optimization tools, not default patterns you need everywhere.
4) Forcing a rerender with key
When a component is wrapped with memo, React may skip re-rendering it if the props are unchanged.
If you want React to treat it as a fresh component instance, you can change its key.
<Counter key={initialCount} initialCount={initialCount} />
Changing the key tells React that this is a different element instance, so it will re-render it.
This is useful when you intentionally want a component to reset or rebuild.
5) Summary
-
memooptimizes component rendering when props stay the same -
useCallbackstabilizes function props -
useMemostabilizes object or array values - use these only when needed; do not add them everywhere without a reason
-
keyis a separate tool for forcing a component to remount or rerender
The main idea is simple: memoization helps avoid unnecessary work when the inputs have not changed. It is a performance tool, not a replacement for bad component structure or unnecessary state.
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.