Why React Components Re-render Too Often
Cover photo by Nangialai Stoman on Unsplash
The core problem
React re-renders happen by default when a component’s state changes or when its parent re-renders. It is how React ensures your UI stays in sync with your data. The trouble starts when components re-render when they absolutely do not need to. If you notice your application feeling sluggish, or your browser profiler shows a cascade of updates for a simple input change, you are likely dealing with unnecessary re-renders.
Passing new object references
The most common culprit is passing new object or array references as props. In JavaScript, an empty object ’{}’ is not equal to another empty object ’{}’. React compares props using shallow equality. When you create an object inside your component body, you create a brand-new reference every time that component runs.
function Parent() { // This object is recreated on every render const style = { color: 'blue' }; return <Child style={style} />;}Because ‘style’ is a new reference, ‘Child’ will always re-render, even if the content inside the object stays the same. To fix this, define static objects outside the component or use ‘useMemo’ if the value depends on state.
The trap of anonymous functions
Similar to objects, anonymous functions are recreated on every render. If you pass an inline arrow function to a child component, that child receives a new function reference every time.
function List() { return ( <ul> {items.map(item => ( <ListItem key={item.id} onClick={() => console.log(item.id)} /> ))} </ul> );}In this example, every ‘ListItem’ re-renders whenever ‘List’ renders, even if the items did not change. You can solve this by moving the handler outside or by wrapping the component in ‘React.memo’ and the handler in ‘useCallback’.
Overusing state at the top
Developers often put state in the highest common ancestor to share it with children. While this is sometimes necessary, it causes every child in that tree to re-render whenever the state changes. If a piece of state is only used by a small branch of your component tree, move that state down. Keep your state as close to the component that uses it as possible.
Misusing React.memo
Wrapping everything in ‘React.memo’ sounds like a good strategy, but it carries a cost. ‘React.memo’ performs a shallow comparison of props. If your props are complex objects that change frequently, the cost of the comparison might outweigh the cost of re-rendering. Only use it when you have evidence that a specific component is causing performance bottlenecks.
Debugging is your friend
Before you start optimizing, check your assumptions. Use the React DevTools Profiler to see exactly why a component re-rendered. If the dev tools say ‘props changed’, look for those object references or functions I mentioned earlier. If it says ‘parent rendered’, look at your component composition and see if you can move state lower.
Optimizing React is not about avoiding renders at all costs. It is about avoiding wasted work. Keep your components simple, avoid unnecessary re-creations of data, and watch your bundle stay lean.