React Performance: Stop Rendering Everything!
Why your app feels like a PowerPoint presentation on a 2005 laptop. A guide to making it fast.
“Premature optimization is the root of all evil.” - Donald Knuth
“My app takes 10 seconds to load a button, is that premature optimization?” - You, probably.
React is fast. JavaScript is fast. Your code… well, let’s talk about that.
The “Rerender” Problem
React’s job is to keep the UI in sync with the state. When state changes, React rerenders. This is fine. This is how it works.
But sometimes, React rerenders too much.
Imagine you have a Header component and a DataList component. You type in the search bar in the Header.
function App() {
const [search, setSearch] = useState("");
const [data, setData] = useState(bigDataList);
return (
<div>
<Header search={search} onSearch={setSearch} />
<DataList data={data} />
</div>
);
}
Every time you type a letter, App rerenders. Because App rerenders, DataList rerenders. Even though data hasn’t changed!
React.memo: The Bouncer
To stop this, we use memo.
const DataList = React.memo(({ data }) => {
// render expensive list
});
Now, DataList only rerenders if data changes. It’s like a bouncer at a club checking IDs. “Name’s not on the list? You’re not coming in (rendering).”
useMemo: The Cache King
Sometimes you have an expensive calculation.
const sortedData = data.sort((a, b) => expensiveSort(a, b));
If you do this in the render body, you are sorting that array on every single render. That’s like cleaning your entire house every time you open the front door.
Use useMemo:
const sortedData = useMemo(() => {
return data.sort((a, b) => expensiveSort(a, b));
}, [data]);
Now, we only sort when data changes. The rest of the time, we just return the cached result.
useCallback: The Function Freezer
Remember useEffect pitfalls? Functions are recreated on every render.
If you pass a function to a child component, the child sees a “new” prop and rerenders, even if you used React.memo!
const handleClick = useCallback(() => {
console.log("Clicked");
}, []);
Now handleClick is the same function across renders, until the dependency array changes.
Conclusion: Don’t Obsess, But Don’t Be Lazy
You don’t need to memoize everything. In fact, memoizing simple things can actually be slower than just re-rendering them.
But if your app feels sluggish, open the React DevTools Profiler. It will show you exactly which component is re-rendering and why. It’s like an X-ray for your code.
Stop making your users’ fans spin. Happy optimizing!