[{"data":1,"prerenderedAt":1194},["ShallowReactive",2],{"content:\u002Fframework-performance\u002Freact-rendering-performance\u002Fwhen-usememo-and-usecallback-actually-help":3,"surroundings:\u002Fframework-performance\u002Freact-rendering-performance\u002Fwhen-usememo-and-usecallback-actually-help":1185},{"id":4,"title":5,"body":6,"description":1165,"extension":1166,"meta":1167,"navigation":1178,"path":1179,"seo":1180,"stem":1183,"__hash__":1184},"content\u002Fframework-performance\u002Freact-rendering-performance\u002Fwhen-usememo-and-usecallback-actually-help\u002Findex.md","When useMemo and useCallback Actually Help",{"type":7,"value":8,"toc":1147},"minimark",[9,13,37,44,215,220,253,257,263,272,278,284,288,293,468,472,654,658,661,665,678,804,808,811,815,834,838,848,912,916,926,930,956,960,966,972,978,984,988,1000,1009,1018,1027,1036,1045,1058,1067,1082,1096,1105,1109,1132,1137,1140,1143],[10,11,5],"h1",{"id":12},"when-usememo-and-usecallback-actually-help",[14,15,16,17,22,23,27,28,32,33,36],"p",{},"This guide is part of ",[18,19,21],"a",{"href":20},"\u002Fframework-performance\u002Freact-rendering-performance\u002F","React Rendering Performance",", within ",[18,24,26],{"href":25},"\u002Fframework-performance\u002F","Framework Performance",". ",[29,30,31],"code",{},"useMemo"," caches the result of a calculation between renders; ",[29,34,35],{},"useCallback"," caches a function's identity. Both are widely used and widely misused. Wrapped around cheap calculations or callbacks passed to plain DOM elements, they add code, comparison overhead and memory without saving anything. Used in the right places, they prevent expensive recalculation and keep memoized children from re-rendering.",[14,38,39,40,43],{},"There are exactly three situations where they help: the calculation itself is expensive; the value or function is passed to a child wrapped in ",[29,41,42],{},"React.memo"," (so a stable identity lets the child skip rendering); or the value is a dependency of an effect or another hook, where a new identity would re-run it. Outside these cases, they are noise. The React Compiler automates most of this, but understanding the rules still matters for code the compiler cannot optimise and for reading profiles.",[14,45,46],{},[47,48,54,55,54,62,54,66,54,69,54,87,54,95,54,101,54,108,54,115,54,119,54,125,54,130,54,139,54,146,54,150,54,156,54,160,54,164,54,167,54,170,54,173,54,176,54,180,54,183,54,186,54,190,54,192,54,195,54,198,54,201,54,205,54,208,54,211,54],"svg",{"viewBox":49,"width":50,"role":51,"ariaLabel":52,"style":53},"0 0 760 342","100%","img","Decision sequence for whether a value or function benefits from memoization.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[56,57],"rect",{"className":58,"x":60,"y":60,"width":50,"height":50,"fill":61},[59],"svg-canvas","0","#ffffff",[63,64,65],"title",{},"Should I wrap this in useMemo or useCallback?",[67,68,52],"desc",{},[70,71,72],"defs",{},[73,74,81],"marker",{"id":75,"viewBox":76,"refX":77,"refY":78,"markerWidth":79,"markerHeight":79,"orient":80},"facdab9010","0 0 10 10","9","5","7","auto-start-reverse",[82,83],"path",{"d":84,"fill":85,"style":86},"M0 0 L10 5 L0 10 z","currentColor","fill-opacity:0.7",[56,88],{"x":89,"y":89,"width":90,"height":91,"rx":92,"fill":93,"stroke":85,"style":94},"1","758","340","10","none","stroke-opacity:0.18",[96,97,65],"text",{"x":98,"y":99,"fill":85,"style":100},"28.0","34.0","font-size:16px;font-weight:700",[56,102],{"x":98,"y":103,"width":104,"height":105,"rx":106,"fill":85,"stroke":85,"style":107},"56.0","320.0","52.0","6","fill-opacity:0.06;stroke-opacity:0.4",[96,109,114],{"x":110,"y":111,"fill":85,"style":112,"textAnchor":113},"188.0","78.0","font-size:13px;font-weight:700","middle","Is the calculation measurably expensive (over",[96,116,118],{"x":110,"y":117,"fill":85,"style":112,"textAnchor":113},"95.0","~1ms)?",[56,120],{"x":121,"y":103,"width":104,"height":105,"rx":106,"fill":122,"stroke":123,"style":124},"412.0","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[96,126,31],{"x":127,"y":128,"fill":85,"style":129,"textAnchor":113},"572.0","86.5","font-size:12.5px",[131,132],"line",{"x1":133,"y1":134,"x2":135,"y2":134,"stroke":85,"strokeWidth":136,"style":137,"markerEnd":138},"348.0","82.0","410.0","1.5","stroke-opacity:0.6","url(#facdab9010)",[96,140,145],{"x":141,"y":142,"fill":143,"style":144,"textAnchor":113},"380.0","75.0","#0466c8","font-size:11.5px;font-weight:700","yes",[131,147],{"x1":110,"y1":148,"x2":110,"y2":149,"stroke":85,"strokeWidth":136,"style":137,"markerEnd":138},"108.0","142.0",[96,151,155],{"x":152,"y":153,"fill":154,"style":144},"196.0","130.0","#51617a","no",[56,157],{"x":98,"y":158,"width":104,"height":159,"rx":106,"fill":85,"stroke":85,"style":107},"144.0","35.0",[96,161,163],{"x":110,"y":162,"fill":85,"style":112,"textAnchor":113},"166.0","Is it passed to a React.memo child?",[56,165],{"x":121,"y":158,"width":104,"height":159,"rx":106,"fill":143,"stroke":143,"style":166},"fill-opacity:0.14;stroke-opacity:0.9",[96,168,169],{"x":127,"y":162,"fill":85,"style":129,"textAnchor":113},"useMemo or useCallback to keep identity stable",[131,171],{"x1":133,"y1":172,"x2":135,"y2":172,"stroke":85,"strokeWidth":136,"style":137,"markerEnd":138},"161.5",[96,174,145],{"x":141,"y":175,"fill":143,"style":144,"textAnchor":113},"154.5",[131,177],{"x1":110,"y1":178,"x2":110,"y2":179,"stroke":85,"strokeWidth":136,"style":137,"markerEnd":138},"179.0","213.0",[96,181,155],{"x":152,"y":182,"fill":154,"style":144},"201.0",[56,184],{"x":98,"y":185,"width":104,"height":159,"rx":106,"fill":85,"stroke":85,"style":107},"215.0",[96,187,189],{"x":110,"y":188,"fill":85,"style":112,"textAnchor":113},"237.0","Is it an effect or hook dependency?",[56,191],{"x":121,"y":185,"width":104,"height":159,"rx":106,"fill":143,"stroke":143,"style":166},[96,193,194],{"x":127,"y":188,"fill":85,"style":129,"textAnchor":113},"Memoize to avoid re-running the effect",[131,196],{"x1":133,"y1":197,"x2":135,"y2":197,"stroke":85,"strokeWidth":136,"style":137,"markerEnd":138},"232.5",[96,199,145],{"x":141,"y":200,"fill":143,"style":144,"textAnchor":113},"225.5",[131,202],{"x1":110,"y1":203,"x2":110,"y2":204,"stroke":85,"strokeWidth":136,"style":137,"markerEnd":138},"250.0","284.0",[96,206,155],{"x":152,"y":207,"fill":154,"style":144},"272.0",[56,209],{"x":98,"y":210,"width":104,"height":99,"rx":106,"fill":122,"stroke":123,"style":124},"286.0",[96,212,214],{"x":110,"y":213,"fill":85,"style":129,"textAnchor":113},"307.5","Do not memoize — it only adds overhead",[216,217,219],"h2",{"id":218},"rapid-diagnosis","Rapid Diagnosis",[221,222,223,231,241,247],"ul",{},[224,225,226,230],"li",{},[227,228,229],"strong",{},"Profile first",": use the React Profiler to find expensive components and why they re-render.",[224,232,233,236,237,240],{},[227,234,235],{},"Time suspicious calculations",": wrap them with ",[29,238,239],{},"console.time"," in a production-like build.",[224,242,243,246],{},[227,244,245],{},"Check whether children are memoized",": stable props only matter for memoized children.",[224,248,249,252],{},[227,250,251],{},"Look for effects re-running"," on every render due to object or function dependencies.",[216,254,256],{"id":255},"root-cause-analysis","Root Cause Analysis",[14,258,259,262],{},[227,260,261],{},"1. Expensive derived data recomputed every render."," Sorting, filtering or aggregating large arrays.",[14,264,265,268,269,271],{},[227,266,267],{},"2. Memoized children receiving new props."," Inline objects and callbacks defeat ",[29,270,42],{},".",[14,273,274,277],{},[227,275,276],{},"3. Effects with unstable dependencies."," Re-subscribing or refetching on every render.",[14,279,280,283],{},[227,281,282],{},"4. Over-memoization."," Memo wrappers everywhere make code harder to read with no gain.",[216,285,287],{"id":286},"step-by-step-resolution","Step-by-Step Resolution",[289,290,292],"h3",{"id":291},"_1-memoize-expensive-derived-data","1. Memoize expensive derived data",[294,295,300],"pre",{"className":296,"code":297,"language":298,"meta":299,"style":299},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","function OrdersTable({ orders, filter }) {\n  const visible = useMemo(\n    () => orders.filter((o) => o.status === filter).sort((a, b) => b.total - a.total),\n    [orders, filter],\n  );\n  return \u003CTable rows={visible} \u002F>;\n}\n\u002F\u002F trade-off: useMemo keeps the previous result in memory and compares deps each\n\u002F\u002F render; worthwhile here because filtering and sorting thousands of rows is slow.\n","javascript","",[29,301,302,331,350,409,415,421,449,455,462],{"__ignoreMap":299},[303,304,306,310,314,318,322,325,328],"span",{"class":131,"line":305},1,[303,307,309],{"class":308},"sPARh","function",[303,311,313],{"class":312},"smZ65"," OrdersTable",[303,315,317],{"class":316},"saISM","({ ",[303,319,321],{"class":320},"sQw3B","orders",[303,323,324],{"class":316},", ",[303,326,327],{"class":320},"filter",[303,329,330],{"class":316}," }) {\n",[303,332,334,337,341,344,347],{"class":131,"line":333},2,[303,335,336],{"class":308},"  const",[303,338,340],{"class":339},"sPXB4"," visible",[303,342,343],{"class":308}," =",[303,345,346],{"class":312}," useMemo",[303,348,349],{"class":316},"(\n",[303,351,353,356,359,362,364,367,370,373,375,378,381,384,387,389,391,393,396,398,400,403,406],{"class":131,"line":352},3,[303,354,355],{"class":316},"    () ",[303,357,358],{"class":308},"=>",[303,360,361],{"class":316}," orders.",[303,363,327],{"class":312},[303,365,366],{"class":316},"((",[303,368,369],{"class":320},"o",[303,371,372],{"class":316},") ",[303,374,358],{"class":308},[303,376,377],{"class":316}," o.status ",[303,379,380],{"class":308},"===",[303,382,383],{"class":316}," filter).",[303,385,386],{"class":312},"sort",[303,388,366],{"class":316},[303,390,18],{"class":320},[303,392,324],{"class":316},[303,394,395],{"class":320},"b",[303,397,372],{"class":316},[303,399,358],{"class":308},[303,401,402],{"class":316}," b.total ",[303,404,405],{"class":308},"-",[303,407,408],{"class":316}," a.total),\n",[303,410,412],{"class":131,"line":411},4,[303,413,414],{"class":316},"    [orders, filter],\n",[303,416,418],{"class":131,"line":417},5,[303,419,420],{"class":316},"  );\n",[303,422,424,427,430,434,437,440,443,446],{"class":131,"line":423},6,[303,425,426],{"class":308},"  return",[303,428,429],{"class":316}," \u003C",[303,431,433],{"class":432},"sZBmE","Table",[303,435,436],{"class":339}," rows",[303,438,439],{"class":308},"={",[303,441,442],{"class":316},"visible",[303,444,445],{"class":308},"}",[303,447,448],{"class":316}," \u002F>;\n",[303,450,452],{"class":131,"line":451},7,[303,453,454],{"class":316},"}\n",[303,456,458],{"class":131,"line":457},8,[303,459,461],{"class":460},"sjfSM","\u002F\u002F trade-off: useMemo keeps the previous result in memory and compares deps each\n",[303,463,465],{"class":131,"line":464},9,[303,466,467],{"class":460},"\u002F\u002F render; worthwhile here because filtering and sorting thousands of rows is slow.\n",[289,469,471],{"id":470},"_2-stabilise-props-for-memoized-children","2. Stabilise props for memoized children",[294,473,475],{"className":296,"code":474,"language":298,"meta":299,"style":299},"const Row = React.memo(function Row({ order, onSelect }) { \u002F* ... *\u002F });\nfunction OrdersTable({ orders }) {\n  const [selected, setSelected] = useState(null);\n  const onSelect = useCallback((id) => setSelected(id), []);   \u002F\u002F stable identity\n  return orders.map((o) => \u003CRow key={o.id} order={o} onSelect={onSelect} \u002F>);\n}\n\u002F\u002F trade-off: without React.memo on Row, useCallback here would do nothing useful.\n",[29,476,477,519,531,563,593,645,649],{"__ignoreMap":299},[303,478,479,482,485,487,490,493,496,498,500,502,505,507,510,513,516],{"class":131,"line":305},[303,480,481],{"class":308},"const",[303,483,484],{"class":339}," Row",[303,486,343],{"class":308},[303,488,489],{"class":316}," React.",[303,491,492],{"class":312},"memo",[303,494,495],{"class":316},"(",[303,497,309],{"class":308},[303,499,484],{"class":312},[303,501,317],{"class":316},[303,503,504],{"class":320},"order",[303,506,324],{"class":316},[303,508,509],{"class":320},"onSelect",[303,511,512],{"class":316}," }) { ",[303,514,515],{"class":460},"\u002F* ... *\u002F",[303,517,518],{"class":316}," });\n",[303,520,521,523,525,527,529],{"class":131,"line":333},[303,522,309],{"class":308},[303,524,313],{"class":312},[303,526,317],{"class":316},[303,528,321],{"class":320},[303,530,330],{"class":316},[303,532,533,535,538,541,543,546,549,552,555,557,560],{"class":131,"line":352},[303,534,336],{"class":308},[303,536,537],{"class":316}," [",[303,539,540],{"class":339},"selected",[303,542,324],{"class":316},[303,544,545],{"class":339},"setSelected",[303,547,548],{"class":316},"] ",[303,550,551],{"class":308},"=",[303,553,554],{"class":312}," useState",[303,556,495],{"class":316},[303,558,559],{"class":339},"null",[303,561,562],{"class":316},");\n",[303,564,565,567,570,572,575,577,580,582,584,587,590],{"class":131,"line":411},[303,566,336],{"class":308},[303,568,569],{"class":339}," onSelect",[303,571,343],{"class":308},[303,573,574],{"class":312}," useCallback",[303,576,366],{"class":316},[303,578,579],{"class":320},"id",[303,581,372],{"class":316},[303,583,358],{"class":308},[303,585,586],{"class":312}," setSelected",[303,588,589],{"class":316},"(id), []);   ",[303,591,592],{"class":460},"\u002F\u002F stable identity\n",[303,594,595,597,599,602,604,606,608,610,612,615,618,620,623,625,628,630,632,634,636,638,640,642],{"class":131,"line":417},[303,596,426],{"class":308},[303,598,361],{"class":316},[303,600,601],{"class":312},"map",[303,603,366],{"class":316},[303,605,369],{"class":320},[303,607,372],{"class":316},[303,609,358],{"class":308},[303,611,429],{"class":316},[303,613,614],{"class":432},"Row",[303,616,617],{"class":339}," key",[303,619,439],{"class":308},[303,621,622],{"class":316},"o.id",[303,624,445],{"class":308},[303,626,627],{"class":339}," order",[303,629,439],{"class":308},[303,631,369],{"class":316},[303,633,445],{"class":308},[303,635,569],{"class":339},[303,637,439],{"class":308},[303,639,509],{"class":316},[303,641,445],{"class":308},[303,643,644],{"class":316}," \u002F>);\n",[303,646,647],{"class":131,"line":423},[303,648,454],{"class":316},[303,650,651],{"class":131,"line":451},[303,652,653],{"class":460},"\u002F\u002F trade-off: without React.memo on Row, useCallback here would do nothing useful.\n",[289,655,657],{"id":656},"_3-stabilise-effect-dependencies","3. Stabilise effect dependencies",[14,659,660],{},"Memoize objects or functions used in effect dependency arrays when a new identity would re-run the effect (re-subscribing, refetching), or move the object creation inside the effect.",[289,662,664],{"id":663},"_4-remove-memoization-that-does-nothing","4. Remove memoization that does nothing",[14,666,667,668,670,671,673,674,677],{},"Delete ",[29,669,31],{}," around trivial expressions and ",[29,672,35],{}," for handlers passed to DOM elements (",[29,675,676],{},"\u003Cbutton onClick>","), which do not benefit from stable identities.",[14,679,680],{},[47,681,54,684,54,687,54,690,54,692,54,695,54,697,54,701,54,708,54,712,54,716,54,719,54,723,54,727,54,731,54,733,54,736,54,738,54,741,54,744,54,748,54,750,54,752,54,754,54,757,54,760,54,764,54,766,54,768,54,770,54,773,54,776,54,780,54,782,54,784,54,786,54,789,54,792,54,795,54,797,54,799,54,801,54],{"viewBox":682,"width":50,"role":51,"ariaLabel":683,"style":53},"0 0 760 258","Common memoization uses and whether they help performance.",[56,685],{"className":686,"x":60,"y":60,"width":50,"height":50,"fill":61},[59],[63,688,689],{},"Memoization cases",[67,691,683],{},[56,693],{"x":89,"y":89,"width":90,"height":694,"rx":92,"fill":93,"stroke":85,"style":94},"256",[96,696,689],{"x":98,"y":99,"fill":85,"style":100},[56,698],{"x":98,"y":103,"width":699,"height":700,"rx":60,"fill":85,"stroke":85,"style":107},"191.4","30.0",[96,702,707],{"x":703,"y":704,"fill":85,"style":705,"textAnchor":706},"38.0","75.5","font-size:12.5px;font-weight:700","start","Case",[56,709],{"x":710,"y":103,"width":711,"height":700,"rx":60,"fill":85,"stroke":85,"style":107},"219.4","256.3",[96,713,715],{"x":714,"y":704,"fill":85,"style":705,"textAnchor":113},"347.6","Helps?",[56,717],{"x":718,"y":103,"width":711,"height":700,"rx":60,"fill":85,"stroke":85,"style":107},"475.7",[96,720,722],{"x":721,"y":704,"fill":85,"style":705,"textAnchor":113},"603.9","Why",[56,724],{"x":98,"y":725,"width":699,"height":700,"rx":60,"fill":93,"stroke":85,"style":726},"86.0","stroke-opacity:0.35",[96,728,730],{"x":703,"y":729,"fill":85,"style":705,"textAnchor":706},"105.5","Filtering\u002Fsorting 5,000 items",[56,732],{"x":710,"y":725,"width":711,"height":700,"rx":60,"fill":143,"stroke":143,"style":166},[96,734,145],{"x":714,"y":729,"fill":85,"style":735,"textAnchor":113},"font-size:12px",[56,737],{"x":718,"y":725,"width":711,"height":700,"rx":60,"fill":93,"stroke":85,"style":726},[96,739,740],{"x":721,"y":729,"fill":85,"style":735,"textAnchor":113},"avoids expensive recomputation",[56,742],{"x":98,"y":743,"width":699,"height":700,"rx":60,"fill":93,"stroke":85,"style":726},"116.0",[96,745,747],{"x":703,"y":746,"fill":85,"style":705,"textAnchor":706},"135.5","Callback to React.memo child",[56,749],{"x":710,"y":743,"width":711,"height":700,"rx":60,"fill":143,"stroke":143,"style":166},[96,751,145],{"x":714,"y":746,"fill":85,"style":735,"textAnchor":113},[56,753],{"x":718,"y":743,"width":711,"height":700,"rx":60,"fill":93,"stroke":85,"style":726},[96,755,756],{"x":721,"y":746,"fill":85,"style":735,"textAnchor":113},"child can skip rendering",[56,758],{"x":98,"y":759,"width":699,"height":700,"rx":60,"fill":93,"stroke":85,"style":726},"146.0",[96,761,763],{"x":703,"y":762,"fill":85,"style":705,"textAnchor":706},"165.5","Object in effect deps",[56,765],{"x":710,"y":759,"width":711,"height":700,"rx":60,"fill":143,"stroke":143,"style":166},[96,767,145],{"x":714,"y":762,"fill":85,"style":735,"textAnchor":113},[56,769],{"x":718,"y":759,"width":711,"height":700,"rx":60,"fill":93,"stroke":85,"style":726},[96,771,772],{"x":721,"y":762,"fill":85,"style":735,"textAnchor":113},"effect does not re-run",[56,774],{"x":98,"y":775,"width":699,"height":700,"rx":60,"fill":93,"stroke":85,"style":726},"176.0",[96,777,779],{"x":703,"y":778,"fill":85,"style":705,"textAnchor":706},"195.5","Callback to a button element",[56,781],{"x":710,"y":775,"width":711,"height":700,"rx":60,"fill":122,"stroke":123,"style":124},[96,783,155],{"x":714,"y":778,"fill":85,"style":735,"textAnchor":113},[56,785],{"x":718,"y":775,"width":711,"height":700,"rx":60,"fill":93,"stroke":85,"style":726},[96,787,788],{"x":721,"y":778,"fill":85,"style":735,"textAnchor":113},"DOM elements do not compare props",[56,790],{"x":98,"y":791,"width":699,"height":700,"rx":60,"fill":93,"stroke":85,"style":726},"206.0",[96,793,794],{"x":703,"y":200,"fill":85,"style":705,"textAnchor":706},"a + b, string concatenation",[56,796],{"x":710,"y":791,"width":711,"height":700,"rx":60,"fill":122,"stroke":123,"style":124},[96,798,155],{"x":714,"y":200,"fill":85,"style":735,"textAnchor":113},[56,800],{"x":718,"y":791,"width":711,"height":700,"rx":60,"fill":93,"stroke":85,"style":726},[96,802,803],{"x":721,"y":200,"fill":85,"style":735,"textAnchor":113},"cheaper than the memo bookkeeping",[216,805,807],{"id":806},"verification","Verification",[14,809,810],{},"Re-profile the interaction: memoized children should show as not rendered, and expensive calculations should disappear from flame charts on unrelated updates. Check that effects no longer re-run unnecessarily (log or count). Confirm in the Performance panel that interaction time decreased.",[216,812,814],{"id":813},"worked-example-an-analytics-dashboard","Worked Example: An Analytics Dashboard",[14,816,817,818,820,821,824,825,827,828,830,831,833],{},"An analytics dashboard recalculated aggregates for six charts from a 20,000-row dataset on every render, including renders caused by hovering over chart tooltips. Each hover cost 120ms. Wrapping the aggregation in ",[29,819,31],{}," keyed on the dataset and date range removed the recalculation from hover renders; memoizing the chart components and stabilising their ",[29,822,823],{},"onHover"," callbacks with ",[29,826,35],{}," stopped five of six charts re-rendering on each hover. Hover interactions dropped to 8ms. A later code review removed 40 other ",[29,829,31],{}," and ",[29,832,35],{}," calls around trivial values, which had no measurable effect on performance and made the code easier to read.",[216,835,837],{"id":836},"how-the-react-compiler-changes-this","How the React Compiler Changes This",[14,839,840,841,324,843,830,845,847],{},"The React Compiler memoizes automatically: it caches JSX elements, computed values and callbacks based on their inputs, at a finer granularity than manual hooks. With the compiler enabled, most manual ",[29,842,31],{},[29,844,35],{},[29,846,42],{}," calls become unnecessary (they remain harmless). Manual memoization is still relevant for code the compiler skips (components that break the Rules of React), for very expensive calculations where you want explicit control, and in codebases not yet using the compiler.",[14,849,850],{},[47,851,54,854,54,857,54,860,54,862,54,865,54,867,54,874,54,881,54,886,54,890,54,895,54,899,54,903,54,908,54],{"viewBox":852,"width":50,"role":51,"ariaLabel":853,"style":53},"0 0 760 163","Bar chart comparing interaction time for a tooltip hover before and after targeted memoization.",[56,855],{"className":856,"x":60,"y":60,"width":50,"height":50,"fill":61},[59],[63,858,859],{},"Tooltip hover cost on an analytics dashboard",[67,861,853],{},[56,863],{"x":89,"y":89,"width":90,"height":864,"rx":92,"fill":93,"stroke":85,"style":94},"161",[96,866,859],{"x":98,"y":99,"fill":85,"style":100},[96,868,873],{"x":869,"y":870,"fill":85,"style":871,"textAnchor":872},"258.5","70.0","font-size:13px","end","No memoization",[56,875],{"x":876,"y":103,"width":877,"height":878,"rx":879,"fill":93,"stroke":85,"style":880},"270.5","397.5","19","3","fill-opacity:0.7;stroke-opacity:0.35",[96,882,885],{"x":883,"y":870,"fill":85,"style":884},"674.0","font-size:12px;font-weight:600","120ms",[96,887,889],{"x":869,"y":888,"fill":85,"style":871,"textAnchor":872},"101.0","useMemo on aggregates",[56,891],{"x":876,"y":892,"width":893,"height":878,"rx":879,"fill":143,"stroke":143,"style":894},"87.0","132.5","fill-opacity:0.55;stroke-opacity:0.9",[96,896,898],{"x":897,"y":888,"fill":85,"style":884},"409.0","40ms",[96,900,902],{"x":869,"y":901,"fill":85,"style":871,"textAnchor":872},"132.0","+ memoized charts and stable callbacks",[56,904],{"x":876,"y":905,"width":906,"height":878,"rx":879,"fill":122,"stroke":123,"style":907},"118.0","26.5","fill-opacity:0.7;stroke-opacity:0.9",[96,909,911],{"x":910,"y":901,"fill":85,"style":884},"303.0","8ms",[216,913,915],{"id":914},"measuring-before-and-after","Measuring Before and After",[14,917,918,919,922,923,925],{},"Treat memoization like any other optimisation: measure first, change one thing, measure again. In the React Profiler, record the interaction and note the component's render time and how often it renders. Add the memoization, record again, and confirm that the component now shows as not rendered (or renders faster). If nothing changes, remove the memoization — it was not addressing the actual cost. For calculations, wrap them temporarily with ",[29,920,921],{},"performance.now()"," timing in a production build on a throttled device; a calculation that takes 0.1ms does not need ",[29,924,31],{},", however large the array looks in the code.",[216,927,929],{"id":928},"common-mistakes","Common Mistakes",[221,931,932,938,944,950],{},[224,933,934,937],{},[227,935,936],{},"Memoizing without React.memo on the child."," Stable props are pointless if the child always re-renders.",[224,939,940,943],{},[227,941,942],{},"Wrong dependency arrays."," Missing dependencies cause stale values; extra ones cause recomputation.",[224,945,946,949],{},[227,947,948],{},"Memoizing cheap work."," Adds overhead and complexity.",[224,951,952,955],{},[227,953,954],{},"Using useMemo for semantic guarantees."," React may discard cached values; do not rely on it for correctness.",[216,957,959],{"id":958},"edge-cases","Edge Cases",[14,961,962,965],{},[227,963,964],{},"Context values."," Memoize context provider values so consumers do not re-render on every provider render.",[14,967,968,971],{},[227,969,970],{},"Custom hooks."," Return stable objects and functions from custom hooks that are used in dependencies elsewhere.",[14,973,974,977],{},[227,975,976],{},"Lists."," Memoizing list items helps most when the list re-renders often with mostly unchanged items.",[14,979,980,983],{},[227,981,982],{},"Large objects."," Memoization keeps old results alive until dependencies change; be mindful of memory for very large data.",[216,985,987],{"id":986},"faq","FAQ",[989,990,993,997],"details",{"className":991},[992],"faq-item",[994,995,996],"summary",{},"Does useCallback make functions faster?",[14,998,999],{},"No. It only keeps the same function identity between renders, which matters for memoized children and dependency arrays.",[989,1001,1003,1006],{"className":1002},[992],[994,1004,1005],{},"Is useMemo free?",[14,1007,1008],{},"No. It compares dependencies every render and stores results. For cheap calculations, the overhead can exceed the savings.",[989,1010,1012,1015],{"className":1011},[992],[994,1013,1014],{},"How expensive must a calculation be to memoize?",[14,1016,1017],{},"A rough threshold is around 1ms or more per render on target devices, or calculations on large collections. Measure with throttling.",[989,1019,1021,1024],{"className":1020},[992],[994,1022,1023],{},"Should I memoize context values?",[14,1025,1026],{},"Yes. A provider value created inline changes identity every render and re-renders all consumers.",[989,1028,1030,1033],{"className":1029},[992],[994,1031,1032],{},"Do I still need these hooks with the React Compiler?",[14,1034,1035],{},"Mostly not. The compiler memoizes automatically; keep manual hooks only where the compiler cannot optimise or explicit control is needed.",[989,1037,1039,1042],{"className":1038},[992],[994,1040,1041],{},"Why does my memoized child still re-render?",[14,1043,1044],{},"One of its props changes identity each render (an inline object, array or callback), or it consumes a context that changes.",[989,1046,1048,1051],{"className":1047},[992],[994,1049,1050],{},"Can memoization cause bugs?",[14,1052,1053,1054,1057],{},"Incorrect dependency arrays can produce stale values. The ",[29,1055,1056],{},"react-hooks\u002Fexhaustive-deps"," lint rule catches most of these mistakes.",[989,1059,1061,1064],{"className":1060},[992],[994,1062,1063],{},"Should I memoize event handlers on DOM elements?",[14,1065,1066],{},"No. DOM elements do not skip work based on prop identity, so stable handlers bring no benefit there.",[989,1068,1070,1073],{"className":1069},[992],[994,1071,1072],{},"Does useMemo help with expensive child components?",[14,1074,1075,1076,1078,1079,1081],{},"Indirectly. Memoizing a JSX element with ",[29,1077,31],{}," (or wrapping the child in ",[29,1080,42],{},") lets React skip re-rendering that subtree when its inputs are unchanged.",[989,1083,1085,1088],{"className":1084},[992],[994,1086,1087],{},"How do I memoize values in custom hooks?",[14,1089,1090,1091,830,1093,1095],{},"Use ",[29,1092,31],{},[29,1094,35],{}," for values and functions the hook returns, so consumers can use them in dependency arrays and memoized children without causing extra work.",[989,1097,1099,1102],{"className":1098},[992],[994,1100,1101],{},"Is it bad to memoize everything just in case?",[14,1103,1104],{},"It is rarely harmful to performance in a big way, but it adds code, memory and dependency arrays to maintain. Targeted memoization, or the compiler, gives the benefit with less noise.",[216,1106,1108],{"id":1107},"related","Related",[221,1110,1111,1118,1125],{},[224,1112,1113,1117],{},[18,1114,1116],{"href":1115},"\u002Fframework-performance\u002Freact-rendering-performance\u002Ffinding-wasted-renders-with-the-react-profiler\u002F","Finding wasted renders with the React Profiler"," — find where memoization matters.",[224,1119,1120,1124],{},[18,1121,1123],{"href":1122},"\u002Fframework-performance\u002Freact-rendering-performance\u002Fadopting-the-react-compiler\u002F","Adopting the React Compiler"," — automatic memoization.",[224,1126,1127,1131],{},[18,1128,1130],{"href":1129},"\u002Fframework-performance\u002Freact-rendering-performance\u002Fstopping-context-re-render-cascades\u002F","Stopping context re-render cascades"," — memoizing context values.",[1133,1134,1136],"script",{"type":1135},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"When useMemo and useCallback Actually Help\",\n  \"description\": \"A practical guide to the cases where useMemo and useCallback improve React performance, the cases where they add overhead, and how to tell the difference.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Memoize expensive derived data\",\n      \"text\": \"Memoize expensive derived data\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Stabilise props for memoized children\",\n      \"text\": \"Stabilise props for memoized children\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Stabilise effect dependencies\",\n      \"text\": \"Memoize objects or functions used in effect dependency arrays when a new identity would re-run the effect (re-subscribing, refetching), or move the object creation inside the effect.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Remove memoization that does nothing\",\n      \"text\": \"Delete useMemo around trivial expressions and useCallback for handlers passed to DOM elements (), which do not benefit from stable identities.\"\n    }\n  ]\n}\n",[1133,1138,1139],{"type":1135},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"When useMemo and useCallback Actually Help\",\n  \"description\": \"A practical guide to the cases where useMemo and useCallback improve React performance, the cases where they add overhead, and how to tell the difference.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fframework-performance\u002Freact-rendering-performance\u002Fwhen-usememo-and-usecallback-actually-help\u002F\"\n  }\n}\n",[1133,1141,1142],{"type":1135},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Framework Performance\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fframework-performance\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"React Rendering Performance\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fframework-performance\u002Freact-rendering-performance\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"When useMemo and useCallback Actually Help\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fframework-performance\u002Freact-rendering-performance\u002Fwhen-usememo-and-usecallback-actually-help\u002F\"\n    }\n  ]\n}\n",[1144,1145,1146],"style",{},"html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .sZBmE, html code.shiki .sZBmE{--shiki-default:#024C1A;--shiki-dark:#72F088;--shiki-light:#024C1A}html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}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);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":299,"searchDepth":333,"depth":333,"links":1148},[1149,1150,1151,1157,1158,1159,1160,1161,1162,1163,1164],{"id":218,"depth":333,"text":219},{"id":255,"depth":333,"text":256},{"id":286,"depth":333,"text":287,"children":1152},[1153,1154,1155,1156],{"id":291,"depth":352,"text":292},{"id":470,"depth":352,"text":471},{"id":656,"depth":352,"text":657},{"id":663,"depth":352,"text":664},{"id":806,"depth":333,"text":807},{"id":813,"depth":333,"text":814},{"id":836,"depth":333,"text":837},{"id":914,"depth":333,"text":915},{"id":928,"depth":333,"text":929},{"id":958,"depth":333,"text":959},{"id":986,"depth":333,"text":987},{"id":1107,"depth":333,"text":1108},"A practical guide to the cases where useMemo and useCallback improve React performance, the cases where they add overhead, and how to tell the difference.","md",{"slug":12,"type":1168,"breadcrumb":1169,"datePublished":1177,"dateModified":1177},"article",[1170,1173,1174,1175],{"name":1171,"url":1172},"Home","\u002F",{"name":26,"url":25},{"name":21,"url":20},{"name":5,"url":1176},"\u002Fframework-performance\u002Freact-rendering-performance\u002Fwhen-usememo-and-usecallback-actually-help\u002F","2026-10-06",true,"\u002Fframework-performance\u002Freact-rendering-performance\u002Fwhen-usememo-and-usecallback-actually-help",{"title":1181,"description":1182},"When useMemo and useCallback Actually Help Performance","useMemo and useCallback help only for expensive calculations and props passed to memoized children or effect dependencies. Learn when to use them and when not to.","framework-performance\u002Freact-rendering-performance\u002Fwhen-usememo-and-usecallback-actually-help\u002Findex","a2mRypAt84UMXGpBYlpbLAHXTiUfQW8o84qrm7-l45k",[1186,1190],{"title":1187,"path":1188,"stem":1189},"useTransition and useDeferredValue for INP","\u002Fframework-performance\u002Freact-rendering-performance\u002Fusetransition-and-usedeferredvalue-for-inp","framework-performance\u002Freact-rendering-performance\u002Fusetransition-and-usedeferredvalue-for-inp\u002Findex",{"title":1191,"path":1192,"stem":1193},"Vue & Nuxt Performance","\u002Fframework-performance\u002Fvue-and-nuxt-performance","framework-performance\u002Fvue-and-nuxt-performance\u002Findex",1791308076419]