How React.memo uses shallow comparison to skip rendering unchanged child components.
By default, React re-renders a component whenever its parent re-renders β regardless of whether the component's own props changed. React.memo is a higher-order component that wraps a functional component and memoizes its render output. It performs a shallow comparison of the previous and new props, and skips re-rendering if they are equal.
When a parent component's state changes, React calls the parent's function. Then it processes all JSX children returned β calling their functions too, even if their props are identical. This cascades down the tree.
React.memo(Component) returns a new component. Before calling Component's function, React checks whether the new props are shallowly equal to the previous props. If yes, React reuses the previous render output β skipping the function call entirely.
For each prop key, React compares oldProp === newProp. Primitives (string, number, boolean) compare by value. Objects and functions compare by reference β a new object literal '{''}' in props always fails the equality check.
React.memo(Component, arePropsEqual) accepts a second argument β a custom comparison function. Return true to skip re-render (props are equal), false to allow it. Useful for deep equality checks or ignoring specific props.
React.memo only skips re-renders caused by prop changes. If the component calls useContext(), it will still re-render when the context value changes β regardless of React.memo.
HOC that memoizes a component's render output. Skips re-render when props are shallowly equal to previous render.
Compares each prop with Object.is(). Effective for primitives. Objects and functions must maintain the same reference across renders.
When React.memo determines props are equal, it returns the previous render output and skips calling the component function.
Keeping the same object/function reference across renders. Required for React.memo to work correctly with non-primitive props.
A re-render where the output is identical to the previous render. React.memo prevents these when props haven't logically changed.
1// Without memo β re-renders every time parent renders2function ExpensiveList({ items }) {3 return items.map(item => <Item key={item.id} {...item} />);4}56// With memo β skips re-render if items reference is stable7const ExpensiveList = React.memo(function ExpensiveList({ items }) {8 return items.map(item => <Item key={item.id} {...item} />);9});1011function Parent() {12 const [count, setCount] = useState(0);1314 // β New array every render β memo never helps15 const items = [{ id: 1, name: 'Apple' }];1617 // β Stable reference β memo works as expected18 const items = useMemo(() => [{ id: 1, name: 'Apple' }], []);1920 return (21 <>22 <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>23 <ExpensiveList items={items} /> {/* skips re-render when only count changes */}24 </>25 );26}
In large React trees with complex components, unnecessary re-renders accumulate into real performance problems β slow interactions, dropped frames, laggy animations. React.memo lets you surgically prevent specific components from re-rendering, keeping expensive subtrees stable while the rest of the app updates freely.
A Slack-like app renders a message list. When a user types in the input box, the entire list of 1000+ message components re-renders because the parent (ChatRoom) updates on every keystroke.
Each keystroke updates the parent's `inputValue` state, triggering a re-render. Without React.memo, all 1000 Message components re-render even though none of their props changed. Each Message component does string formatting and date calculations β the combined cost creates visible lag.
Wrap Message in React.memo: `const Message = React.memo(({ text, author, timestamp }) => { ... })`. Since the message props are primitive values (strings, numbers), React.memo's shallow comparison correctly identifies that nothing changed. The 1000 messages are skipped entirely β only the input re-renders.
Takeaway: React.memo shines when a parent re-renders frequently but a child's props rarely change. The ideal candidates are list items, complex charts, and any component with expensive rendering where the parent updates for unrelated reasons.
A MapWidget receives a `coordinates` object `{ lat: 40.7, lng: -74.0 }`. Despite wrapping in React.memo, it still re-renders because the parent creates a new coordinates object each render.
React.memo uses shallow comparison by default: `oldCoords !== newCoords` is always true because each render creates a new object, even if lat/lng values are identical. Shallow comparison checks reference equality, not value equality.
Provide a custom comparison: `React.memo(MapWidget, (prev, next) => prev.coordinates.lat === next.coordinates.lat && prev.coordinates.lng === next.coordinates.lng)`. Now React.memo compares the actual values instead of the object reference. Alternatively, flatten the props: `<MapWidget lat={40.7} lng={-74.0} />` β primitives compare by value automatically.
Takeaway: Use custom areEqual functions for components that receive objects or arrays as props and can't be easily memoized upstream. But first, consider if you can flatten the props to primitives or memoize the object with useMemo in the parent β these are simpler and more maintainable.