[{"data":1,"prerenderedAt":1168},["ShallowReactive",2],{"blog-\u002Fblog\u002Freact-memo-guide":3},{"id":4,"title":5,"body":6,"description":1154,"difficulty":1155,"extension":1156,"framework":1157,"frameworkSlug":1158,"meta":1159,"navigation":144,"order":87,"path":1160,"qaPath":1161,"seo":1162,"stem":1163,"subtopic":23,"topic":1164,"topicSlug":1165,"updated":1166,"__hash__":1167},"blog\u002Fblog\u002Freact-memo-guide.md","React.memo — A Complete Guide with Examples",{"type":7,"value":8,"toc":1143},"minimark",[9,14,28,159,162,166,183,296,302,306,312,334,337,341,354,518,527,692,700,704,715,855,865,869,882,984,991,995,1002,1042,1050,1054,1085,1089,1120,1139],[10,11,13],"h2",{"id":12},"what-does-reactmemo-do","What Does React.memo Do?",[15,16,17,24,25,27],"p",{},[18,19,20],"strong",{},[21,22,23],"code",{},"React.memo"," is a higher-order component that wraps a function component and prevents it from re-rendering unless its props actually change. By default React re-renders a child whenever the parent re-renders — regardless of whether the child's props changed. ",[21,26,23],{}," adds a prop-equality check before calling the component function again.",[29,30,35],"pre",{"className":31,"code":32,"language":33,"meta":34,"style":34},"language-jsx shiki shiki-themes github-light github-dark","const Avatar = React.memo(function Avatar({ name, src }) {\n  console.log('Avatar rendered');\n  return \u003Cimg src={src} alt={name} \u002F>;\n});\n\n\u002F\u002F Avatar only re-renders when `name` or `src` changes,\n\u002F\u002F not every time the parent re-renders.\n","jsx","",[21,36,37,85,103,133,139,146,153],{"__ignoreMap":34},[38,39,42,46,50,53,57,61,64,67,69,72,76,79,82],"span",{"class":40,"line":41},"line",1,[38,43,45],{"class":44},"szBVR","const",[38,47,49],{"class":48},"sj4cs"," Avatar",[38,51,52],{"class":44}," =",[38,54,56],{"class":55},"sVt8B"," React.",[38,58,60],{"class":59},"sScJk","memo",[38,62,63],{"class":55},"(",[38,65,66],{"class":44},"function",[38,68,49],{"class":59},[38,70,71],{"class":55},"({ ",[38,73,75],{"class":74},"s4XuR","name",[38,77,78],{"class":55},", ",[38,80,81],{"class":74},"src",[38,83,84],{"class":55}," }) {\n",[38,86,88,91,94,96,100],{"class":40,"line":87},2,[38,89,90],{"class":55},"  console.",[38,92,93],{"class":59},"log",[38,95,63],{"class":55},[38,97,99],{"class":98},"sZZnC","'Avatar rendered'",[38,101,102],{"class":55},");\n",[38,104,106,109,112,116,119,122,125,128,130],{"class":40,"line":105},3,[38,107,108],{"class":44},"  return",[38,110,111],{"class":55}," \u003C",[38,113,115],{"class":114},"s9eBZ","img",[38,117,118],{"class":59}," src",[38,120,121],{"class":44},"=",[38,123,124],{"class":55},"{src} ",[38,126,127],{"class":59},"alt",[38,129,121],{"class":44},[38,131,132],{"class":55},"{name} \u002F>;\n",[38,134,136],{"class":40,"line":135},4,[38,137,138],{"class":55},"});\n",[38,140,142],{"class":40,"line":141},5,[38,143,145],{"emptyLinePlaceholder":144},true,"\n",[38,147,149],{"class":40,"line":148},6,[38,150,152],{"class":151},"sJ8bj","\u002F\u002F Avatar only re-renders when `name` or `src` changes,\n",[38,154,156],{"class":40,"line":155},7,[38,157,158],{"class":151},"\u002F\u002F not every time the parent re-renders.\n",[15,160,161],{},"If the props are equal, React reuses the last rendered output. If they differ, it re-renders normally.",[10,163,165],{"id":164},"shallow-equality-what-equal-means","Shallow Equality — What \"Equal\" Means",[15,167,168,170,171,174,175,178,179,182],{},[21,169,23],{}," uses ",[18,172,173],{},"shallow equality"," by default: it compares each prop with ",[21,176,177],{},"Object.is",". This works perfectly for primitives (strings, numbers, booleans) but trips up on ",[18,180,181],{},"non-primitive props"," like objects, arrays, and functions, because those are compared by reference, not by value.",[29,184,186],{"className":31,"code":185,"language":33,"meta":34,"style":34},"\u002F\u002F This breaks memo — a new object is created on every parent render\nfunction Parent() {\n  return \u003CChart options={{ color: 'blue' }} \u002F>; \u002F\u002F new ref each render\n}\n\n\u002F\u002F Fix: lift the constant out of the render function\nconst OPTIONS = { color: 'blue' };\nfunction Parent() {\n  return \u003CChart options={OPTIONS} \u002F>;\n}\n",[21,187,188,193,203,229,234,238,243,260,269,291],{"__ignoreMap":34},[38,189,190],{"class":40,"line":41},[38,191,192],{"class":151},"\u002F\u002F This breaks memo — a new object is created on every parent render\n",[38,194,195,197,200],{"class":40,"line":87},[38,196,66],{"class":44},[38,198,199],{"class":59}," Parent",[38,201,202],{"class":55},"() {\n",[38,204,205,207,209,212,215,217,220,223,226],{"class":40,"line":105},[38,206,108],{"class":44},[38,208,111],{"class":55},[38,210,211],{"class":48},"Chart",[38,213,214],{"class":59}," options",[38,216,121],{"class":44},[38,218,219],{"class":55},"{{ color: ",[38,221,222],{"class":98},"'blue'",[38,224,225],{"class":55}," }} \u002F>; ",[38,227,228],{"class":151},"\u002F\u002F new ref each render\n",[38,230,231],{"class":40,"line":135},[38,232,233],{"class":55},"}\n",[38,235,236],{"class":40,"line":141},[38,237,145],{"emptyLinePlaceholder":144},[38,239,240],{"class":40,"line":148},[38,241,242],{"class":151},"\u002F\u002F Fix: lift the constant out of the render function\n",[38,244,245,247,250,252,255,257],{"class":40,"line":155},[38,246,45],{"class":44},[38,248,249],{"class":48}," OPTIONS",[38,251,52],{"class":44},[38,253,254],{"class":55}," { color: ",[38,256,222],{"class":98},[38,258,259],{"class":55}," };\n",[38,261,263,265,267],{"class":40,"line":262},8,[38,264,66],{"class":44},[38,266,199],{"class":59},[38,268,202],{"class":55},[38,270,272,274,276,278,280,282,285,288],{"class":40,"line":271},9,[38,273,108],{"class":44},[38,275,111],{"class":55},[38,277,211],{"class":48},[38,279,214],{"class":59},[38,281,121],{"class":44},[38,283,284],{"class":55},"{",[38,286,287],{"class":48},"OPTIONS",[38,289,290],{"class":55},"} \u002F>;\n",[38,292,294],{"class":40,"line":293},10,[38,295,233],{"class":55},[15,297,298,299,301],{},"Understanding shallow equality is the most important concept for ",[21,300,23],{}," interview questions — examiners often ask \"why isn't memo working?\" and the answer almost always comes down to unstable references.",[10,303,305],{"id":304},"when-is-reactmemo-worth-using","When Is React.memo Worth Using?",[15,307,308,309,311],{},"Three conditions should all be true before reaching for ",[21,310,23],{},":",[313,314,315,322,328],"ol",{},[316,317,318,321],"li",{},[18,319,320],{},"The component re-renders frequently"," — ideally confirmed with the React DevTools Profiler, not assumed.",[316,323,324,327],{},[18,325,326],{},"The component is expensive to render"," — complex DOM output, heavy calculations inside the component body, or deep subtrees.",[316,329,330,333],{},[18,331,332],{},"The props are stable"," — primitives or memoized references that don't change on every parent render.",[15,335,336],{},"Apply it at the boundary where data flows down into a subtree that doesn't need to update with every parent change — for example, a sidebar that only cares about the current user, not the rest of the page state.",[10,338,340],{"id":339},"the-inline-function-pitfall","The Inline-Function Pitfall",[15,342,343,344,346,347,350,351,353],{},"The most common ",[21,345,23],{}," bug: passing an ",[18,348,349],{},"inline callback"," as a prop. A new function reference is created on every parent render, making ",[21,352,23],{},"'s equality check always fail.",[29,355,357],{"className":31,"code":356,"language":33,"meta":34,"style":34},"\u002F\u002F Broken — onClick is a new reference every render\nfunction Parent() {\n  const [count, setCount] = useState(0);\n  return (\n    \u003C>\n      \u003Cbutton onClick={() => setCount(c => c + 1)}>Increment\u003C\u002Fbutton>\n      {\u002F* memo does nothing — handleClick is recreated every render *\u002F}\n      \u003CExpensiveList onItemClick={(id) => console.log(id)} \u002F>\n    \u003C\u002F>\n  );\n}\n",[21,358,359,364,372,403,410,415,462,472,503,508,513],{"__ignoreMap":34},[38,360,361],{"class":40,"line":41},[38,362,363],{"class":151},"\u002F\u002F Broken — onClick is a new reference every render\n",[38,365,366,368,370],{"class":40,"line":87},[38,367,66],{"class":44},[38,369,199],{"class":59},[38,371,202],{"class":55},[38,373,374,377,380,383,385,388,391,393,396,398,401],{"class":40,"line":105},[38,375,376],{"class":44},"  const",[38,378,379],{"class":55}," [",[38,381,382],{"class":48},"count",[38,384,78],{"class":55},[38,386,387],{"class":48},"setCount",[38,389,390],{"class":55},"] ",[38,392,121],{"class":44},[38,394,395],{"class":59}," useState",[38,397,63],{"class":55},[38,399,400],{"class":48},"0",[38,402,102],{"class":55},[38,404,405,407],{"class":40,"line":135},[38,406,108],{"class":44},[38,408,409],{"class":55}," (\n",[38,411,412],{"class":40,"line":141},[38,413,414],{"class":55},"    \u003C>\n",[38,416,417,420,423,426,428,431,434,437,439,442,445,448,451,454,457,459],{"class":40,"line":148},[38,418,419],{"class":55},"      \u003C",[38,421,422],{"class":114},"button",[38,424,425],{"class":59}," onClick",[38,427,121],{"class":44},[38,429,430],{"class":55},"{() ",[38,432,433],{"class":44},"=>",[38,435,436],{"class":59}," setCount",[38,438,63],{"class":55},[38,440,441],{"class":74},"c",[38,443,444],{"class":44}," =>",[38,446,447],{"class":55}," c ",[38,449,450],{"class":44},"+",[38,452,453],{"class":48}," 1",[38,455,456],{"class":55},")}>Increment\u003C\u002F",[38,458,422],{"class":114},[38,460,461],{"class":55},">\n",[38,463,464,467,470],{"class":40,"line":155},[38,465,466],{"class":55},"      {",[38,468,469],{"class":151},"\u002F* memo does nothing — handleClick is recreated every render *\u002F",[38,471,233],{"class":55},[38,473,474,476,479,482,484,487,490,493,495,498,500],{"class":40,"line":262},[38,475,419],{"class":55},[38,477,478],{"class":48},"ExpensiveList",[38,480,481],{"class":59}," onItemClick",[38,483,121],{"class":44},[38,485,486],{"class":55},"{(",[38,488,489],{"class":74},"id",[38,491,492],{"class":55},") ",[38,494,433],{"class":44},[38,496,497],{"class":55}," console.",[38,499,93],{"class":59},[38,501,502],{"class":55},"(id)} \u002F>\n",[38,504,505],{"class":40,"line":271},[38,506,507],{"class":55},"    \u003C\u002F>\n",[38,509,510],{"class":40,"line":293},[38,511,512],{"class":55},"  );\n",[38,514,516],{"class":40,"line":515},11,[38,517,233],{"class":55},[15,519,520,521,526],{},"The fix is ",[18,522,523],{},[21,524,525],{},"useCallback",", which memoizes the function reference across renders:",[29,528,530],{"className":31,"code":529,"language":33,"meta":34,"style":34},"function Parent() {\n  const [count, setCount] = useState(0);\n\n  \u002F\u002F Stable reference — only changes if deps change\n  const handleItemClick = useCallback((id) => {\n    console.log(id);\n  }, []); \u002F\u002F empty deps → created once\n\n  return (\n    \u003C>\n      \u003Cbutton onClick={() => setCount(c => c + 1)}>Increment\u003C\u002Fbutton>\n      \u003CExpensiveList onItemClick={handleItemClick} \u002F>\n    \u003C\u002F>\n  );\n}\n",[21,531,532,540,564,568,573,597,607,615,619,625,629,663,677,682,687],{"__ignoreMap":34},[38,533,534,536,538],{"class":40,"line":41},[38,535,66],{"class":44},[38,537,199],{"class":59},[38,539,202],{"class":55},[38,541,542,544,546,548,550,552,554,556,558,560,562],{"class":40,"line":87},[38,543,376],{"class":44},[38,545,379],{"class":55},[38,547,382],{"class":48},[38,549,78],{"class":55},[38,551,387],{"class":48},[38,553,390],{"class":55},[38,555,121],{"class":44},[38,557,395],{"class":59},[38,559,63],{"class":55},[38,561,400],{"class":48},[38,563,102],{"class":55},[38,565,566],{"class":40,"line":105},[38,567,145],{"emptyLinePlaceholder":144},[38,569,570],{"class":40,"line":135},[38,571,572],{"class":151},"  \u002F\u002F Stable reference — only changes if deps change\n",[38,574,575,577,580,582,585,588,590,592,594],{"class":40,"line":141},[38,576,376],{"class":44},[38,578,579],{"class":48}," handleItemClick",[38,581,52],{"class":44},[38,583,584],{"class":59}," useCallback",[38,586,587],{"class":55},"((",[38,589,489],{"class":74},[38,591,492],{"class":55},[38,593,433],{"class":44},[38,595,596],{"class":55}," {\n",[38,598,599,602,604],{"class":40,"line":148},[38,600,601],{"class":55},"    console.",[38,603,93],{"class":59},[38,605,606],{"class":55},"(id);\n",[38,608,609,612],{"class":40,"line":155},[38,610,611],{"class":55},"  }, []); ",[38,613,614],{"class":151},"\u002F\u002F empty deps → created once\n",[38,616,617],{"class":40,"line":262},[38,618,145],{"emptyLinePlaceholder":144},[38,620,621,623],{"class":40,"line":271},[38,622,108],{"class":44},[38,624,409],{"class":55},[38,626,627],{"class":40,"line":293},[38,628,414],{"class":55},[38,630,631,633,635,637,639,641,643,645,647,649,651,653,655,657,659,661],{"class":40,"line":515},[38,632,419],{"class":55},[38,634,422],{"class":114},[38,636,425],{"class":59},[38,638,121],{"class":44},[38,640,430],{"class":55},[38,642,433],{"class":44},[38,644,436],{"class":59},[38,646,63],{"class":55},[38,648,441],{"class":74},[38,650,444],{"class":44},[38,652,447],{"class":55},[38,654,450],{"class":44},[38,656,453],{"class":48},[38,658,456],{"class":55},[38,660,422],{"class":114},[38,662,461],{"class":55},[38,664,666,668,670,672,674],{"class":40,"line":665},12,[38,667,419],{"class":55},[38,669,478],{"class":48},[38,671,481],{"class":59},[38,673,121],{"class":44},[38,675,676],{"class":55},"{handleItemClick} \u002F>\n",[38,678,680],{"class":40,"line":679},13,[38,681,507],{"class":55},[38,683,685],{"class":40,"line":684},14,[38,686,512],{"class":55},[38,688,690],{"class":40,"line":689},15,[38,691,233],{"class":55},[15,693,694,696,697,699],{},[21,695,23],{}," and ",[21,698,525],{}," are a pair — one rarely makes sense without the other when callbacks are involved.",[10,701,703],{"id":702},"custom-comparators","Custom Comparators",[15,705,706,707,710,711,714],{},"You can override the default shallow-equality check by passing a second argument: a function that receives the previous and next props and returns ",[21,708,709],{},"true"," if the component should ",[18,712,713],{},"skip"," re-rendering.",[29,716,718],{"className":31,"code":717,"language":33,"meta":34,"style":34},"const Chart = React.memo(\n  function Chart({ data, label }) {\n    return \u003Ccanvas>{\u002F* expensive render *\u002F}\u003C\u002Fcanvas>;\n  },\n  (prevProps, nextProps) => {\n    \u002F\u002F Only re-render when the data array length changes\n    \u002F\u002F (ignoring internal value changes — intentional trade-off)\n    return prevProps.data.length === nextProps.data.length\n      && prevProps.label === nextProps.label;\n  }\n);\n",[21,719,720,736,755,779,784,803,808,813,832,846,851],{"__ignoreMap":34},[38,721,722,724,727,729,731,733],{"class":40,"line":41},[38,723,45],{"class":44},[38,725,726],{"class":48}," Chart",[38,728,52],{"class":44},[38,730,56],{"class":55},[38,732,60],{"class":59},[38,734,735],{"class":55},"(\n",[38,737,738,741,743,745,748,750,753],{"class":40,"line":87},[38,739,740],{"class":44},"  function",[38,742,726],{"class":59},[38,744,71],{"class":55},[38,746,747],{"class":74},"data",[38,749,78],{"class":55},[38,751,752],{"class":74},"label",[38,754,84],{"class":55},[38,756,757,760,762,765,768,771,774,776],{"class":40,"line":105},[38,758,759],{"class":44},"    return",[38,761,111],{"class":55},[38,763,764],{"class":114},"canvas",[38,766,767],{"class":55},">{",[38,769,770],{"class":151},"\u002F* expensive render *\u002F",[38,772,773],{"class":55},"}\u003C\u002F",[38,775,764],{"class":114},[38,777,778],{"class":55},">;\n",[38,780,781],{"class":40,"line":135},[38,782,783],{"class":55},"  },\n",[38,785,786,789,792,794,797,799,801],{"class":40,"line":141},[38,787,788],{"class":55},"  (",[38,790,791],{"class":74},"prevProps",[38,793,78],{"class":55},[38,795,796],{"class":74},"nextProps",[38,798,492],{"class":55},[38,800,433],{"class":44},[38,802,596],{"class":55},[38,804,805],{"class":40,"line":148},[38,806,807],{"class":151},"    \u002F\u002F Only re-render when the data array length changes\n",[38,809,810],{"class":40,"line":155},[38,811,812],{"class":151},"    \u002F\u002F (ignoring internal value changes — intentional trade-off)\n",[38,814,815,817,820,823,826,829],{"class":40,"line":262},[38,816,759],{"class":44},[38,818,819],{"class":55}," prevProps.data.",[38,821,822],{"class":48},"length",[38,824,825],{"class":44}," ===",[38,827,828],{"class":55}," nextProps.data.",[38,830,831],{"class":48},"length\n",[38,833,834,837,840,843],{"class":40,"line":271},[38,835,836],{"class":44},"      &&",[38,838,839],{"class":55}," prevProps.label ",[38,841,842],{"class":44},"===",[38,844,845],{"class":55}," nextProps.label;\n",[38,847,848],{"class":40,"line":293},[38,849,850],{"class":55},"  }\n",[38,852,853],{"class":40,"line":515},[38,854,102],{"class":55},[15,856,857,858,860,861,864],{},"Use custom comparators sparingly. They are easy to get wrong (returning ",[21,859,709],{}," when you should return ",[21,862,863],{},"false"," silently skips valid updates), and they add maintenance overhead. Prefer stable prop references over custom logic.",[10,866,868],{"id":867},"context-subscriptions-bypass-reactmemo","Context Subscriptions Bypass React.memo",[15,870,871,872,877,878,881],{},"This is a frequent interview gotcha: ",[18,873,874,876],{},[21,875,23],{}," does not block re-renders triggered by context",". If a memoized component calls ",[21,879,880],{},"useContext",", it will re-render whenever that context value changes — regardless of whether the component's props changed.",[29,883,885],{"className":31,"code":884,"language":33,"meta":34,"style":34},"const ThemeContext = createContext('light');\n\n\u002F\u002F This WILL re-render when ThemeContext changes,\n\u002F\u002F even though it's wrapped in React.memo\nconst Button = React.memo(function Button({ label }) {\n  const theme = useContext(ThemeContext);\n  return \u003Cbutton className={theme}>{label}\u003C\u002Fbutton>;\n});\n",[21,886,887,906,910,915,920,945,960,980],{"__ignoreMap":34},[38,888,889,891,894,896,899,901,904],{"class":40,"line":41},[38,890,45],{"class":44},[38,892,893],{"class":48}," ThemeContext",[38,895,52],{"class":44},[38,897,898],{"class":59}," createContext",[38,900,63],{"class":55},[38,902,903],{"class":98},"'light'",[38,905,102],{"class":55},[38,907,908],{"class":40,"line":87},[38,909,145],{"emptyLinePlaceholder":144},[38,911,912],{"class":40,"line":105},[38,913,914],{"class":151},"\u002F\u002F This WILL re-render when ThemeContext changes,\n",[38,916,917],{"class":40,"line":135},[38,918,919],{"class":151},"\u002F\u002F even though it's wrapped in React.memo\n",[38,921,922,924,927,929,931,933,935,937,939,941,943],{"class":40,"line":141},[38,923,45],{"class":44},[38,925,926],{"class":48}," Button",[38,928,52],{"class":44},[38,930,56],{"class":55},[38,932,60],{"class":59},[38,934,63],{"class":55},[38,936,66],{"class":44},[38,938,926],{"class":59},[38,940,71],{"class":55},[38,942,752],{"class":74},[38,944,84],{"class":55},[38,946,947,949,952,954,957],{"class":40,"line":148},[38,948,376],{"class":44},[38,950,951],{"class":48}," theme",[38,953,52],{"class":44},[38,955,956],{"class":59}," useContext",[38,958,959],{"class":55},"(ThemeContext);\n",[38,961,962,964,966,968,971,973,976,978],{"class":40,"line":155},[38,963,108],{"class":44},[38,965,111],{"class":55},[38,967,422],{"class":114},[38,969,970],{"class":59}," className",[38,972,121],{"class":44},[38,974,975],{"class":55},"{theme}>{label}\u003C\u002F",[38,977,422],{"class":114},[38,979,778],{"class":55},[38,981,982],{"class":40,"line":262},[38,983,138],{"class":55},[15,985,986,987,990],{},"The fix is to split contexts so components only subscribe to the slice they need, or to use context selectors (a pattern enabled by libraries like ",[21,988,989],{},"use-context-selector",").",[10,992,994],{"id":993},"the-children-prop-gotcha","The Children Prop Gotcha",[15,996,997,998,1001],{},"Passing JSX as ",[21,999,1000],{},"children"," creates a new React element object on every parent render, which defeats memo just like an inline object prop would.",[29,1003,1005],{"className":31,"code":1004,"language":33,"meta":34,"style":34},"\u002F\u002F memo does nothing — children is a new element object each render\n\u003CMemoizedWrapper>\n  \u003CSomeContent \u002F>\n\u003C\u002FMemoizedWrapper>\n",[21,1006,1007,1012,1022,1033],{"__ignoreMap":34},[38,1008,1009],{"class":40,"line":41},[38,1010,1011],{"class":151},"\u002F\u002F memo does nothing — children is a new element object each render\n",[38,1013,1014,1017,1020],{"class":40,"line":87},[38,1015,1016],{"class":55},"\u003C",[38,1018,1019],{"class":48},"MemoizedWrapper",[38,1021,461],{"class":55},[38,1023,1024,1027,1030],{"class":40,"line":105},[38,1025,1026],{"class":55},"  \u003C",[38,1028,1029],{"class":48},"SomeContent",[38,1031,1032],{"class":55}," \u002F>\n",[38,1034,1035,1038,1040],{"class":40,"line":135},[38,1036,1037],{"class":55},"\u003C\u002F",[38,1039,1019],{"class":48},[38,1041,461],{"class":55},[15,1043,1044,1045,1047,1048,990],{},"If you need to memoize a component that accepts ",[21,1046,1000],{},", either memoize the children separately or restructure so the wrapper accepts a render prop (a stable function reference via ",[21,1049,525],{},[10,1051,1053],{"id":1052},"class-component-equivalent-purecomponent","Class Component Equivalent: PureComponent",[15,1055,1056,1058,1059,1062,1063,1066,1067,1070,1071,1074,1075,1077,1078,1081,1082,1084],{},[21,1057,23],{}," is the function-component equivalent of ",[21,1060,1061],{},"React.PureComponent",". Both do a ",[18,1064,1065],{},"shallow prop comparison"," to skip unnecessary renders. ",[21,1068,1069],{},"PureComponent"," also does a shallow ",[18,1072,1073],{},"state"," comparison, which has no equivalent in ",[21,1076,23],{}," (state comparison in function components is handled inside ",[21,1079,1080],{},"useState"," itself — React bails out of a re-render automatically when the new state is ",[21,1083,177],{},"-equal to the old one).",[10,1086,1088],{"id":1087},"when-not-to-use-reactmemo","When NOT to Use React.memo",[1090,1091,1092,1102,1108,1114],"ul",{},[316,1093,1094,1097,1098,1101],{},[18,1095,1096],{},"Cheap components"," — the equality check itself has a cost. For a component that renders a single ",[21,1099,1100],{},"\u003Cspan>",", memo adds overhead without benefit.",[316,1103,1104,1107],{},[18,1105,1106],{},"Components that almost always receive new props"," — memo just wastes the comparison.",[316,1109,1110,1113],{},[18,1111,1112],{},"Components near the root"," — they rarely re-render in the first place; the win is negligible.",[316,1115,1116,1119],{},[18,1117,1118],{},"As a first resort"," — always profile before memoizing. Premature memoization adds cognitive overhead and can hide architectural problems (state living too high in the tree).",[15,1121,1122,1125,1126,1128,1129,1131,1132,1135,1136,1138],{},[18,1123,1124],{},"Rule of thumb:"," reach for ",[21,1127,23],{}," only after the DevTools Profiler shows a component re-rendering more than it should and you have confirmed its props can be made stable. Fix unstable references with ",[21,1130,525],{}," (functions) and ",[21,1133,1134],{},"useMemo"," (objects\u002Farrays), then wrap with ",[21,1137,23],{}," as the final step.",[1140,1141,1142],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":34,"searchDepth":87,"depth":87,"links":1144},[1145,1146,1147,1148,1149,1150,1151,1152,1153],{"id":12,"depth":87,"text":13},{"id":164,"depth":87,"text":165},{"id":304,"depth":87,"text":305},{"id":339,"depth":87,"text":340},{"id":702,"depth":87,"text":703},{"id":867,"depth":87,"text":868},{"id":993,"depth":87,"text":994},{"id":1052,"depth":87,"text":1053},{"id":1087,"depth":87,"text":1088},"React.memo interview questions — when to memoize components, shallow equality, custom comparators, pitfalls, and when memo actually hurts performance.","medium","md","React","react",{},"\u002Fblog\u002Freact-memo-guide","\u002Freact\u002Frendering-and-performance\u002Freact-memo",{"title":5,"description":1154},"blog\u002Freact-memo-guide","Rendering and Performance","rendering-and-performance","2026-06-24","e3pR30y3Qpkos4KgepEb14QDQkS-RYjOB5lrnlKN6l4",1782244083220]