Optimizing React Components
Most React performance problems aren't React problems. They're component design problems. You don't need a profiler to know that re-rendering 500 componen...
14 Oct 2023

Most React performance problems aren't React problems. They're component design problems. You don't need a profiler to know that re-rendering 500 components on every keystroke is going to be slow. The fix is usually simpler than you think.
1. Use functions, not classes
Class components carry overhead. They have lifecycle methods, this binding, and more surface area for bugs. Function components with hooks are lighter, easier to optimize, and easier to reason about.
Instead of this:
class ProfilePage extends React.Component {
showMessage = () => {
alert('Followed ' + this.props.user)
}
render() {
return <button onClick={this.showMessage}>Follow</button>
}
}
Write this:
const ProfilePage: React.FC<{ user: string }> = ({ user }) => {
const showMessage = () => alert('Followed ' + user)
return <button onClick={showMessage}>Follow</button>
}
The function component captures props at render time. No stale this.props bugs. Less code. Easier to optimize with React.memo later.
2. Memoize expensive components
If a component renders the same output for the same props, wrap it in React.memo. React will skip re-rendering it when the parent re-renders but the props haven't changed.
const ExpensiveList = React.memo(({ items }: { items: string[] }) => {
return (
<ul>
{items.map(item => <li key={item}>{item}</li>)}
</ul>
)
})
The trade-off: React.memo adds a shallow comparison on every render. If your props are cheap to compare and the component is expensive to render, it's a win. If the component is already fast, the comparison overhead isn't worth it.
3. Move state down
The biggest optimization isn't a function call. It's architecture. If a state change causes an entire page to re-render, the state is too high in the tree. Push state down to the component that actually needs it.
A text input's local state shouldn't live in a page-level context. A modal's open/closed state shouldn't live in a global store. Every state variable should live as close to its consumers as possible.
4. Avoid inline object and function creation
Every time you write style={{ color: 'red' }} or onClick={() => doThing()} in JSX, you create a new object or function reference on every render. This breaks React.memo comparisons and triggers unnecessary child re-renders.
Extract stable references:
const style = { color: 'red' }
const handleClick = useCallback(() => doThing(), [])
return <Button style={style} onClick={handleClick} />
The real advice
Profile before you optimize. React DevTools has a profiler that shows you exactly which components re-render and why. Fix the components that show up in red. Ignore the rest. Premature optimization costs more than the performance problem it's trying to solve.