[{"data":1,"prerenderedAt":2208},["ShallowReactive",2],{"page-\u002Freact\u002F09-usememo-and-usecallback":3},{"id":4,"title":5,"body":6,"description":2201,"extension":2202,"meta":2203,"navigation":65,"path":2204,"seo":2205,"stem":2206,"__hash__":2207},"content\u002Freact\u002F09-usememo-and-usecallback.md","09 — useMemo & useCallback",{"type":7,"value":8,"toc":2190},"minimark",[9,22,27,264,268,461,465,970,974,1244,1248,1515,1519,1623,1627,1736,1740,1748,1925,2094,2098,2187],[10,11,13,14,18,19],"h1",{"id":12},"_09-usememo-usecallback","09 — ",[15,16,17],"code",{},"useMemo"," & ",[15,20,21],{},"useCallback",[23,24,26],"h2",{"id":25},"what-they-do-and-why-they-exist","What They Do and Why They Exist",[28,29,32],"code-wrapper",{"filename":30,"language":31},"memoization_basics.js","javascript",[33,34,38],"pre",{"className":35,"code":36,"language":31,"meta":37,"style":37},"language-javascript shiki shiki-themes github-light github-dark","import { useMemo, useCallback } from 'react'\n\n\u002F\u002F useMemo: memoizes a VALUE. Returns the same reference if deps haven't changed.\nconst sortedList = useMemo(() => items.sort((a, b) => a.name.localeCompare(b.name)), [items])\n\u002F\u002F If items is the same reference → returns the cached sorted array\n\u002F\u002F If items changes → recomputes and returns a new array\n\n\u002F\u002F useCallback: memoizes a FUNCTION reference.\nconst handleClick = useCallback(() => doSomething(id), [id])\n\u002F\u002F If id is the same → returns the same function reference\n\u002F\u002F If id changes → returns a new function\n\n\u002F\u002F EQUIVALENCE: useCallback(fn, deps) === useMemo(() => fn, deps)\n\u002F\u002F useCallback is just shorthand for useMemo with a function return value.\n\n\u002F\u002F WHY THEY EXIST: referential equality.\n\u002F\u002F On every render, inline objects\u002Ffunctions create NEW references:\n\u002F\u002F   {} !== {} (same content, different object)\n\u002F\u002F   () => {} !== () => {} (same logic, different function instance)\n\u002F\u002F These new references break:\n\u002F\u002F   1. React.memo child components (shallow prop comparison fails → re-renders)\n\u002F\u002F   2. useEffect\u002FuseCallback dependency arrays (Object.is fails → effect re-runs)\n\u002F\u002F   3. Context value comparison (new object → all consumers re-render)\n\u002F\u002F useMemo\u002FuseCallback stabilize references so these comparisons succeed.\n","",[15,39,40,60,67,74,130,136,142,147,153,176,182,188,193,199,205,210,216,222,228,234,240,246,252,258],{"__ignoreMap":37},[41,42,45,49,53,56],"span",{"class":43,"line":44},"line",1,[41,46,48],{"class":47},"svdQ7","import",[41,50,52],{"class":51},"ssxIu"," { useMemo, useCallback } ",[41,54,55],{"class":47},"from",[41,57,59],{"class":58},"sJ6F3"," 'react'\n",[41,61,63],{"class":43,"line":62},2,[41,64,66],{"emptyLinePlaceholder":65},true,"\n",[41,68,70],{"class":43,"line":69},3,[41,71,73],{"class":72},"sdCPZ","\u002F\u002F useMemo: memoizes a VALUE. Returns the same reference if deps haven't changed.\n",[41,75,77,80,84,87,91,94,97,100,103,106,110,113,116,119,121,124,127],{"class":43,"line":76},4,[41,78,79],{"class":47},"const",[41,81,83],{"class":82},"snvgF"," sortedList",[41,85,86],{"class":47}," =",[41,88,90],{"class":89},"sIsaT"," useMemo",[41,92,93],{"class":51},"(() ",[41,95,96],{"class":47},"=>",[41,98,99],{"class":51}," items.",[41,101,102],{"class":89},"sort",[41,104,105],{"class":51},"((",[41,107,109],{"class":108},"sCrzJ","a",[41,111,112],{"class":51},", ",[41,114,115],{"class":108},"b",[41,117,118],{"class":51},") ",[41,120,96],{"class":47},[41,122,123],{"class":51}," a.name.",[41,125,126],{"class":89},"localeCompare",[41,128,129],{"class":51},"(b.name)), [items])\n",[41,131,133],{"class":43,"line":132},5,[41,134,135],{"class":72},"\u002F\u002F If items is the same reference → returns the cached sorted array\n",[41,137,139],{"class":43,"line":138},6,[41,140,141],{"class":72},"\u002F\u002F If items changes → recomputes and returns a new array\n",[41,143,145],{"class":43,"line":144},7,[41,146,66],{"emptyLinePlaceholder":65},[41,148,150],{"class":43,"line":149},8,[41,151,152],{"class":72},"\u002F\u002F useCallback: memoizes a FUNCTION reference.\n",[41,154,156,158,161,163,166,168,170,173],{"class":43,"line":155},9,[41,157,79],{"class":47},[41,159,160],{"class":82}," handleClick",[41,162,86],{"class":47},[41,164,165],{"class":89}," useCallback",[41,167,93],{"class":51},[41,169,96],{"class":47},[41,171,172],{"class":89}," doSomething",[41,174,175],{"class":51},"(id), [id])\n",[41,177,179],{"class":43,"line":178},10,[41,180,181],{"class":72},"\u002F\u002F If id is the same → returns the same function reference\n",[41,183,185],{"class":43,"line":184},11,[41,186,187],{"class":72},"\u002F\u002F If id changes → returns a new function\n",[41,189,191],{"class":43,"line":190},12,[41,192,66],{"emptyLinePlaceholder":65},[41,194,196],{"class":43,"line":195},13,[41,197,198],{"class":72},"\u002F\u002F EQUIVALENCE: useCallback(fn, deps) === useMemo(() => fn, deps)\n",[41,200,202],{"class":43,"line":201},14,[41,203,204],{"class":72},"\u002F\u002F useCallback is just shorthand for useMemo with a function return value.\n",[41,206,208],{"class":43,"line":207},15,[41,209,66],{"emptyLinePlaceholder":65},[41,211,213],{"class":43,"line":212},16,[41,214,215],{"class":72},"\u002F\u002F WHY THEY EXIST: referential equality.\n",[41,217,219],{"class":43,"line":218},17,[41,220,221],{"class":72},"\u002F\u002F On every render, inline objects\u002Ffunctions create NEW references:\n",[41,223,225],{"class":43,"line":224},18,[41,226,227],{"class":72},"\u002F\u002F   {} !== {} (same content, different object)\n",[41,229,231],{"class":43,"line":230},19,[41,232,233],{"class":72},"\u002F\u002F   () => {} !== () => {} (same logic, different function instance)\n",[41,235,237],{"class":43,"line":236},20,[41,238,239],{"class":72},"\u002F\u002F These new references break:\n",[41,241,243],{"class":43,"line":242},21,[41,244,245],{"class":72},"\u002F\u002F   1. React.memo child components (shallow prop comparison fails → re-renders)\n",[41,247,249],{"class":43,"line":248},22,[41,250,251],{"class":72},"\u002F\u002F   2. useEffect\u002FuseCallback dependency arrays (Object.is fails → effect re-runs)\n",[41,253,255],{"class":43,"line":254},23,[41,256,257],{"class":72},"\u002F\u002F   3. Context value comparison (new object → all consumers re-render)\n",[41,259,261],{"class":43,"line":260},24,[41,262,263],{"class":72},"\u002F\u002F useMemo\u002FuseCallback stabilize references so these comparisons succeed.\n",[23,265,267],{"id":266},"when-to-use-the-decision-matrix","When to Use: The Decision Matrix",[28,269,271],{"filename":270,"language":31},"when_to_use.js",[33,272,274],{"className":35,"code":273,"language":31,"meta":37,"style":37},"\u002F\u002F USE MEMOIZATION WHEN:\n\u002F\u002F 1. The value\u002Ffunction is passed to a React.memo'd child\n\u002F\u002F 2. The value\u002Ffunction is in a useEffect\u002FuseCallback\u002FuseMemo dependency array\n\u002F\u002F 3. The value is an expensive computation (sorting\u002Ffiltering large datasets)\n\u002F\u002F 4. The value is a context provider value (Ch 7)\n\n\u002F\u002F DON'T USE MEMOIZATION WHEN:\n\u002F\u002F 1. The computation is cheap (arithmetic, simple string ops, small array maps)\n\u002F\u002F    — the memoization overhead (comparison + storage) costs more than the computation\n\u002F\u002F 2. The child isn't React.memo'd — it re-renders on parent render regardless\n\u002F\u002F 3. The value\u002Ffunction is only used in the same component (no dependency array)\n\n\u002F\u002F ANTI-PATTERN: wrapping everything \"just in case\"\nfunction BadComponent({ data }) {\n  const value = useMemo(() => data + 1, [data])  \u002F\u002F addition is FREE — no memo needed\n  const handler = useCallback(() => console.log('click'), [])  \u002F\u002F used only here, no memo needed\n  return \u003Cdiv onClick={handler}>{value}\u003C\u002Fdiv>\n  \u002F\u002F Neither the child (plain div) nor any dependency array needs stable references.\n  \u002F\u002F The useMemo\u002FuseCallback overhead is pure waste.\n}\n",[15,275,276,281,286,291,296,301,305,310,315,320,325,330,334,339,356,387,420,446,451,456],{"__ignoreMap":37},[41,277,278],{"class":43,"line":44},[41,279,280],{"class":72},"\u002F\u002F USE MEMOIZATION WHEN:\n",[41,282,283],{"class":43,"line":62},[41,284,285],{"class":72},"\u002F\u002F 1. The value\u002Ffunction is passed to a React.memo'd child\n",[41,287,288],{"class":43,"line":69},[41,289,290],{"class":72},"\u002F\u002F 2. The value\u002Ffunction is in a useEffect\u002FuseCallback\u002FuseMemo dependency array\n",[41,292,293],{"class":43,"line":76},[41,294,295],{"class":72},"\u002F\u002F 3. The value is an expensive computation (sorting\u002Ffiltering large datasets)\n",[41,297,298],{"class":43,"line":132},[41,299,300],{"class":72},"\u002F\u002F 4. The value is a context provider value (Ch 7)\n",[41,302,303],{"class":43,"line":138},[41,304,66],{"emptyLinePlaceholder":65},[41,306,307],{"class":43,"line":144},[41,308,309],{"class":72},"\u002F\u002F DON'T USE MEMOIZATION WHEN:\n",[41,311,312],{"class":43,"line":149},[41,313,314],{"class":72},"\u002F\u002F 1. The computation is cheap (arithmetic, simple string ops, small array maps)\n",[41,316,317],{"class":43,"line":155},[41,318,319],{"class":72},"\u002F\u002F    — the memoization overhead (comparison + storage) costs more than the computation\n",[41,321,322],{"class":43,"line":178},[41,323,324],{"class":72},"\u002F\u002F 2. The child isn't React.memo'd — it re-renders on parent render regardless\n",[41,326,327],{"class":43,"line":184},[41,328,329],{"class":72},"\u002F\u002F 3. The value\u002Ffunction is only used in the same component (no dependency array)\n",[41,331,332],{"class":43,"line":190},[41,333,66],{"emptyLinePlaceholder":65},[41,335,336],{"class":43,"line":195},[41,337,338],{"class":72},"\u002F\u002F ANTI-PATTERN: wrapping everything \"just in case\"\n",[41,340,341,344,347,350,353],{"class":43,"line":201},[41,342,343],{"class":47},"function",[41,345,346],{"class":89}," BadComponent",[41,348,349],{"class":51},"({ ",[41,351,352],{"class":108},"data",[41,354,355],{"class":51}," }) {\n",[41,357,358,361,364,366,368,370,372,375,378,381,384],{"class":43,"line":207},[41,359,360],{"class":47},"  const",[41,362,363],{"class":82}," value",[41,365,86],{"class":47},[41,367,90],{"class":89},[41,369,93],{"class":51},[41,371,96],{"class":47},[41,373,374],{"class":51}," data ",[41,376,377],{"class":47},"+",[41,379,380],{"class":82}," 1",[41,382,383],{"class":51},", [data])  ",[41,385,386],{"class":72},"\u002F\u002F addition is FREE — no memo needed\n",[41,388,389,391,394,396,398,400,402,405,408,411,414,417],{"class":43,"line":212},[41,390,360],{"class":47},[41,392,393],{"class":82}," handler",[41,395,86],{"class":47},[41,397,165],{"class":89},[41,399,93],{"class":51},[41,401,96],{"class":47},[41,403,404],{"class":51}," console.",[41,406,407],{"class":89},"log",[41,409,410],{"class":51},"(",[41,412,413],{"class":58},"'click'",[41,415,416],{"class":51},"), [])  ",[41,418,419],{"class":72},"\u002F\u002F used only here, no memo needed\n",[41,421,422,425,428,432,435,438,441,443],{"class":43,"line":218},[41,423,424],{"class":47},"  return",[41,426,427],{"class":51}," \u003C",[41,429,431],{"class":430},"sk71V","div",[41,433,434],{"class":89}," onClick",[41,436,437],{"class":47},"=",[41,439,440],{"class":51},"{handler}>{value}\u003C\u002F",[41,442,431],{"class":430},[41,444,445],{"class":51},">\n",[41,447,448],{"class":43,"line":224},[41,449,450],{"class":72},"  \u002F\u002F Neither the child (plain div) nor any dependency array needs stable references.\n",[41,452,453],{"class":43,"line":230},[41,454,455],{"class":72},"  \u002F\u002F The useMemo\u002FuseCallback overhead is pure waste.\n",[41,457,458],{"class":43,"line":236},[41,459,460],{"class":51},"}\n",[23,462,464],{"id":463},"reactmemo-usecallback-usememo-the-production-pattern","React.memo + useCallback + useMemo: The Production Pattern",[28,466,468],{"filename":467,"language":31},"production_memoization.js",[33,469,471],{"className":35,"code":470,"language":31,"meta":37,"style":37},"import { memo, useMemo, useCallback } from 'react'\n\n\u002F\u002F React.memo wraps a component and prevents re-renders when props are shallow-equal\nconst ExpensiveRow = memo(function Row({ item, onSelect, isSelected }) {\n  \u002F\u002F This component does expensive work (or renders many DOM nodes).\n  \u002F\u002F React.memo does a shallow comparison of props: Object.is(prevProp, nextProp)\n  \u002F\u002F If ALL props are the same reference → SKIPS re-render entirely.\n  return (\n    \u003Ctr className={isSelected ? 'selected' : ''} onClick={() => onSelect(item.id)}>\n      \u003Ctd>{item.name}\u003C\u002Ftd>\n      \u003Ctd>{item.price}\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  )\n})\n\nfunction ProductTable({ items, selectedId }) {\n  \u002F\u002F 1. Memoize the sorted\u002Ffiltered data so the array reference is stable\n  const sortedItems = useMemo(\n    () => [...items].sort((a, b) => a.name.localeCompare(b.name)),\n    [items]\n  )\n\n  \u002F\u002F 2. Memoize the handler so the function reference is stable\n  const handleSelect = useCallback((id) => {\n    console.log('Selected:', id)\n  }, [])  \u002F\u002F no deps → stable forever (unless it needs state\u002Fprops)\n\n  \u002F\u002F 3. React.memo on Row + stable props → Row only re-renders when its item changes\n  return (\n    \u003Ctable>\n      \u003Ctbody>\n        {sortedItems.map(item => (\n          \u003CExpensiveRow\n            key={item.id}\n            item={item}\n            onSelect={handleSelect}\n            isSelected={item.id === selectedId}\n          \u002F>\n        ))}\n      \u003C\u002Ftbody>\n    \u003C\u002Ftable>\n  )\n}\n\u002F\u002F Without ALL THREE (memo + useMemo + useCallback), the optimization breaks:\n\u002F\u002F - No React.memo on Row → Row re-renders on every parent render regardless\n\u002F\u002F - No useMemo on sortedItems → new array every render → Row sees new item ref\n\u002F\u002F - No useCallback on handleSelect → new function every render → prop changes → Row re-renders\n",[15,472,473,484,488,493,529,534,539,544,551,598,613,626,635,640,645,649,668,673,687,724,729,733,737,742,765,781,790,795,801,808,818,828,846,855,866,877,888,905,911,917,927,936,941,946,952,958,964],{"__ignoreMap":37},[41,474,475,477,480,482],{"class":43,"line":44},[41,476,48],{"class":47},[41,478,479],{"class":51}," { memo, useMemo, useCallback } ",[41,481,55],{"class":47},[41,483,59],{"class":58},[41,485,486],{"class":43,"line":62},[41,487,66],{"emptyLinePlaceholder":65},[41,489,490],{"class":43,"line":69},[41,491,492],{"class":72},"\u002F\u002F React.memo wraps a component and prevents re-renders when props are shallow-equal\n",[41,494,495,497,500,502,505,507,509,512,514,517,519,522,524,527],{"class":43,"line":76},[41,496,79],{"class":47},[41,498,499],{"class":82}," ExpensiveRow",[41,501,86],{"class":47},[41,503,504],{"class":89}," memo",[41,506,410],{"class":51},[41,508,343],{"class":47},[41,510,511],{"class":89}," Row",[41,513,349],{"class":51},[41,515,516],{"class":108},"item",[41,518,112],{"class":51},[41,520,521],{"class":108},"onSelect",[41,523,112],{"class":51},[41,525,526],{"class":108},"isSelected",[41,528,355],{"class":51},[41,530,531],{"class":43,"line":132},[41,532,533],{"class":72},"  \u002F\u002F This component does expensive work (or renders many DOM nodes).\n",[41,535,536],{"class":43,"line":138},[41,537,538],{"class":72},"  \u002F\u002F React.memo does a shallow comparison of props: Object.is(prevProp, nextProp)\n",[41,540,541],{"class":43,"line":144},[41,542,543],{"class":72},"  \u002F\u002F If ALL props are the same reference → SKIPS re-render entirely.\n",[41,545,546,548],{"class":43,"line":149},[41,547,424],{"class":47},[41,549,550],{"class":51}," (\n",[41,552,553,556,559,562,564,567,570,573,576,579,582,585,587,590,592,595],{"class":43,"line":155},[41,554,555],{"class":51},"    \u003C",[41,557,558],{"class":430},"tr",[41,560,561],{"class":89}," className",[41,563,437],{"class":47},[41,565,566],{"class":51},"{isSelected ",[41,568,569],{"class":47},"?",[41,571,572],{"class":58}," 'selected'",[41,574,575],{"class":47}," :",[41,577,578],{"class":58}," ''",[41,580,581],{"class":51},"} ",[41,583,584],{"class":89},"onClick",[41,586,437],{"class":47},[41,588,589],{"class":51},"{() ",[41,591,96],{"class":47},[41,593,594],{"class":89}," onSelect",[41,596,597],{"class":51},"(item.id)}>\n",[41,599,600,603,606,609,611],{"class":43,"line":178},[41,601,602],{"class":51},"      \u003C",[41,604,605],{"class":430},"td",[41,607,608],{"class":51},">{item.name}\u003C\u002F",[41,610,605],{"class":430},[41,612,445],{"class":51},[41,614,615,617,619,622,624],{"class":43,"line":184},[41,616,602],{"class":51},[41,618,605],{"class":430},[41,620,621],{"class":51},">{item.price}\u003C\u002F",[41,623,605],{"class":430},[41,625,445],{"class":51},[41,627,628,631,633],{"class":43,"line":190},[41,629,630],{"class":51},"    \u003C\u002F",[41,632,558],{"class":430},[41,634,445],{"class":51},[41,636,637],{"class":43,"line":195},[41,638,639],{"class":51},"  )\n",[41,641,642],{"class":43,"line":201},[41,643,644],{"class":51},"})\n",[41,646,647],{"class":43,"line":207},[41,648,66],{"emptyLinePlaceholder":65},[41,650,651,653,656,658,661,663,666],{"class":43,"line":212},[41,652,343],{"class":47},[41,654,655],{"class":89}," ProductTable",[41,657,349],{"class":51},[41,659,660],{"class":108},"items",[41,662,112],{"class":51},[41,664,665],{"class":108},"selectedId",[41,667,355],{"class":51},[41,669,670],{"class":43,"line":218},[41,671,672],{"class":72},"  \u002F\u002F 1. Memoize the sorted\u002Ffiltered data so the array reference is stable\n",[41,674,675,677,680,682,684],{"class":43,"line":224},[41,676,360],{"class":47},[41,678,679],{"class":82}," sortedItems",[41,681,86],{"class":47},[41,683,90],{"class":89},[41,685,686],{"class":51},"(\n",[41,688,689,692,694,697,700,703,705,707,709,711,713,715,717,719,721],{"class":43,"line":230},[41,690,691],{"class":51},"    () ",[41,693,96],{"class":47},[41,695,696],{"class":51}," [",[41,698,699],{"class":47},"...",[41,701,702],{"class":51},"items].",[41,704,102],{"class":89},[41,706,105],{"class":51},[41,708,109],{"class":108},[41,710,112],{"class":51},[41,712,115],{"class":108},[41,714,118],{"class":51},[41,716,96],{"class":47},[41,718,123],{"class":51},[41,720,126],{"class":89},[41,722,723],{"class":51},"(b.name)),\n",[41,725,726],{"class":43,"line":236},[41,727,728],{"class":51},"    [items]\n",[41,730,731],{"class":43,"line":242},[41,732,639],{"class":51},[41,734,735],{"class":43,"line":248},[41,736,66],{"emptyLinePlaceholder":65},[41,738,739],{"class":43,"line":254},[41,740,741],{"class":72},"  \u002F\u002F 2. Memoize the handler so the function reference is stable\n",[41,743,744,746,749,751,753,755,758,760,762],{"class":43,"line":260},[41,745,360],{"class":47},[41,747,748],{"class":82}," handleSelect",[41,750,86],{"class":47},[41,752,165],{"class":89},[41,754,105],{"class":51},[41,756,757],{"class":108},"id",[41,759,118],{"class":51},[41,761,96],{"class":47},[41,763,764],{"class":51}," {\n",[41,766,768,771,773,775,778],{"class":43,"line":767},25,[41,769,770],{"class":51},"    console.",[41,772,407],{"class":89},[41,774,410],{"class":51},[41,776,777],{"class":58},"'Selected:'",[41,779,780],{"class":51},", id)\n",[41,782,784,787],{"class":43,"line":783},26,[41,785,786],{"class":51},"  }, [])  ",[41,788,789],{"class":72},"\u002F\u002F no deps → stable forever (unless it needs state\u002Fprops)\n",[41,791,793],{"class":43,"line":792},27,[41,794,66],{"emptyLinePlaceholder":65},[41,796,798],{"class":43,"line":797},28,[41,799,800],{"class":72},"  \u002F\u002F 3. React.memo on Row + stable props → Row only re-renders when its item changes\n",[41,802,804,806],{"class":43,"line":803},29,[41,805,424],{"class":47},[41,807,550],{"class":51},[41,809,811,813,816],{"class":43,"line":810},30,[41,812,555],{"class":51},[41,814,815],{"class":430},"table",[41,817,445],{"class":51},[41,819,821,823,826],{"class":43,"line":820},31,[41,822,602],{"class":51},[41,824,825],{"class":430},"tbody",[41,827,445],{"class":51},[41,829,831,834,837,839,841,844],{"class":43,"line":830},32,[41,832,833],{"class":51},"        {sortedItems.",[41,835,836],{"class":89},"map",[41,838,410],{"class":51},[41,840,516],{"class":108},[41,842,843],{"class":47}," =>",[41,845,550],{"class":51},[41,847,849,852],{"class":43,"line":848},33,[41,850,851],{"class":51},"          \u003C",[41,853,854],{"class":82},"ExpensiveRow\n",[41,856,858,861,863],{"class":43,"line":857},34,[41,859,860],{"class":89},"            key",[41,862,437],{"class":47},[41,864,865],{"class":51},"{item.id}\n",[41,867,869,872,874],{"class":43,"line":868},35,[41,870,871],{"class":89},"            item",[41,873,437],{"class":47},[41,875,876],{"class":51},"{item}\n",[41,878,880,883,885],{"class":43,"line":879},36,[41,881,882],{"class":89},"            onSelect",[41,884,437],{"class":47},[41,886,887],{"class":51},"{handleSelect}\n",[41,889,891,894,896,899,902],{"class":43,"line":890},37,[41,892,893],{"class":89},"            isSelected",[41,895,437],{"class":47},[41,897,898],{"class":51},"{item.id ",[41,900,901],{"class":47},"===",[41,903,904],{"class":51}," selectedId}\n",[41,906,908],{"class":43,"line":907},38,[41,909,910],{"class":51},"          \u002F>\n",[41,912,914],{"class":43,"line":913},39,[41,915,916],{"class":51},"        ))}\n",[41,918,920,923,925],{"class":43,"line":919},40,[41,921,922],{"class":51},"      \u003C\u002F",[41,924,825],{"class":430},[41,926,445],{"class":51},[41,928,930,932,934],{"class":43,"line":929},41,[41,931,630],{"class":51},[41,933,815],{"class":430},[41,935,445],{"class":51},[41,937,939],{"class":43,"line":938},42,[41,940,639],{"class":51},[41,942,944],{"class":43,"line":943},43,[41,945,460],{"class":51},[41,947,949],{"class":43,"line":948},44,[41,950,951],{"class":72},"\u002F\u002F Without ALL THREE (memo + useMemo + useCallback), the optimization breaks:\n",[41,953,955],{"class":43,"line":954},45,[41,956,957],{"class":72},"\u002F\u002F - No React.memo on Row → Row re-renders on every parent render regardless\n",[41,959,961],{"class":43,"line":960},46,[41,962,963],{"class":72},"\u002F\u002F - No useMemo on sortedItems → new array every render → Row sees new item ref\n",[41,965,967],{"class":43,"line":966},47,[41,968,969],{"class":72},"\u002F\u002F - No useCallback on handleSelect → new function every render → prop changes → Row re-renders\n",[23,971,973],{"id":972},"the-cost-of-memoization","The Cost of Memoization",[28,975,977],{"filename":976,"language":31},"memoization_cost.js",[33,978,980],{"className":35,"code":979,"language":31,"meta":37,"style":37},"\u002F\u002F useMemo\u002FuseCallback are NOT free — they have overhead:\n\u002F\u002F 1. Comparison cost: React runs Object.is on every dep on every render\n\u002F\u002F 2. Storage cost: the memoized value + deps array are stored in fiber\n\u002F\u002F 3. GC pressure: cached values stay in memory until deps change\n\n\u002F\u002F RULE OF THUMB: only memoize when the cost of NOT memoizing > the cost of memoizing\n\u002F\u002F   - NOT memoizing cost: unnecessary re-render of an expensive component\n\u002F\u002F   - Memoizing cost: O(deps) comparison + memory on every render\n\n\u002F\u002F When memoization HURTS:\nfunction CheapList({ items }) {\n  \u002F\u002F Items is a small array (10 items). The sort is trivial.\n  const sorted = useMemo(() => items.sort(), [items])  \u002F\u002F memo overhead > sort cost\n  \u002F\u002F If items changes frequently, the memo never hits cache → pure overhead.\n  \u002F\u002F Just compute inline: const sorted = items.sort()\n  return \u003Cdiv>{sorted.map(i => \u003Cspan key={i}>{i}\u003C\u002Fspan>)}\u003C\u002Fdiv>\n}\n\n\u002F\u002F When memoization HELPS:\nfunction BigList({ items }) {\n  \u002F\u002F 10,000 items. Sort takes 50ms. Parent re-renders frequently (e.g., search box).\n  const sorted = useMemo(() => items.sort((a, b) => a.score - b.score), [items])\n  \u002F\u002F If items doesn't change but parent re-renders → memo saves 50ms per render.\n  return \u003Cdiv>{sorted.map(i => \u003CRow key={i.id} data={i} \u002F>)}\u003C\u002Fdiv>\n}\n",[15,981,982,987,992,997,1002,1006,1011,1016,1021,1025,1030,1043,1048,1073,1078,1083,1124,1128,1132,1137,1150,1155,1194,1199,1240],{"__ignoreMap":37},[41,983,984],{"class":43,"line":44},[41,985,986],{"class":72},"\u002F\u002F useMemo\u002FuseCallback are NOT free — they have overhead:\n",[41,988,989],{"class":43,"line":62},[41,990,991],{"class":72},"\u002F\u002F 1. Comparison cost: React runs Object.is on every dep on every render\n",[41,993,994],{"class":43,"line":69},[41,995,996],{"class":72},"\u002F\u002F 2. Storage cost: the memoized value + deps array are stored in fiber\n",[41,998,999],{"class":43,"line":76},[41,1000,1001],{"class":72},"\u002F\u002F 3. GC pressure: cached values stay in memory until deps change\n",[41,1003,1004],{"class":43,"line":132},[41,1005,66],{"emptyLinePlaceholder":65},[41,1007,1008],{"class":43,"line":138},[41,1009,1010],{"class":72},"\u002F\u002F RULE OF THUMB: only memoize when the cost of NOT memoizing > the cost of memoizing\n",[41,1012,1013],{"class":43,"line":144},[41,1014,1015],{"class":72},"\u002F\u002F   - NOT memoizing cost: unnecessary re-render of an expensive component\n",[41,1017,1018],{"class":43,"line":149},[41,1019,1020],{"class":72},"\u002F\u002F   - Memoizing cost: O(deps) comparison + memory on every render\n",[41,1022,1023],{"class":43,"line":155},[41,1024,66],{"emptyLinePlaceholder":65},[41,1026,1027],{"class":43,"line":178},[41,1028,1029],{"class":72},"\u002F\u002F When memoization HURTS:\n",[41,1031,1032,1034,1037,1039,1041],{"class":43,"line":184},[41,1033,343],{"class":47},[41,1035,1036],{"class":89}," CheapList",[41,1038,349],{"class":51},[41,1040,660],{"class":108},[41,1042,355],{"class":51},[41,1044,1045],{"class":43,"line":190},[41,1046,1047],{"class":72},"  \u002F\u002F Items is a small array (10 items). The sort is trivial.\n",[41,1049,1050,1052,1055,1057,1059,1061,1063,1065,1067,1070],{"class":43,"line":195},[41,1051,360],{"class":47},[41,1053,1054],{"class":82}," sorted",[41,1056,86],{"class":47},[41,1058,90],{"class":89},[41,1060,93],{"class":51},[41,1062,96],{"class":47},[41,1064,99],{"class":51},[41,1066,102],{"class":89},[41,1068,1069],{"class":51},"(), [items])  ",[41,1071,1072],{"class":72},"\u002F\u002F memo overhead > sort cost\n",[41,1074,1075],{"class":43,"line":201},[41,1076,1077],{"class":72},"  \u002F\u002F If items changes frequently, the memo never hits cache → pure overhead.\n",[41,1079,1080],{"class":43,"line":207},[41,1081,1082],{"class":72},"  \u002F\u002F Just compute inline: const sorted = items.sort()\n",[41,1084,1085,1087,1089,1091,1094,1096,1098,1101,1103,1105,1107,1110,1112,1115,1117,1120,1122],{"class":43,"line":212},[41,1086,424],{"class":47},[41,1088,427],{"class":51},[41,1090,431],{"class":430},[41,1092,1093],{"class":51},">{sorted.",[41,1095,836],{"class":89},[41,1097,410],{"class":51},[41,1099,1100],{"class":108},"i",[41,1102,843],{"class":47},[41,1104,427],{"class":51},[41,1106,41],{"class":430},[41,1108,1109],{"class":89}," key",[41,1111,437],{"class":47},[41,1113,1114],{"class":51},"{i}>{i}\u003C\u002F",[41,1116,41],{"class":430},[41,1118,1119],{"class":51},">)}\u003C\u002F",[41,1121,431],{"class":430},[41,1123,445],{"class":51},[41,1125,1126],{"class":43,"line":218},[41,1127,460],{"class":51},[41,1129,1130],{"class":43,"line":224},[41,1131,66],{"emptyLinePlaceholder":65},[41,1133,1134],{"class":43,"line":230},[41,1135,1136],{"class":72},"\u002F\u002F When memoization HELPS:\n",[41,1138,1139,1141,1144,1146,1148],{"class":43,"line":236},[41,1140,343],{"class":47},[41,1142,1143],{"class":89}," BigList",[41,1145,349],{"class":51},[41,1147,660],{"class":108},[41,1149,355],{"class":51},[41,1151,1152],{"class":43,"line":242},[41,1153,1154],{"class":72},"  \u002F\u002F 10,000 items. Sort takes 50ms. Parent re-renders frequently (e.g., search box).\n",[41,1156,1157,1159,1161,1163,1165,1167,1169,1171,1173,1175,1177,1179,1181,1183,1185,1188,1191],{"class":43,"line":248},[41,1158,360],{"class":47},[41,1160,1054],{"class":82},[41,1162,86],{"class":47},[41,1164,90],{"class":89},[41,1166,93],{"class":51},[41,1168,96],{"class":47},[41,1170,99],{"class":51},[41,1172,102],{"class":89},[41,1174,105],{"class":51},[41,1176,109],{"class":108},[41,1178,112],{"class":51},[41,1180,115],{"class":108},[41,1182,118],{"class":51},[41,1184,96],{"class":47},[41,1186,1187],{"class":51}," a.score ",[41,1189,1190],{"class":47},"-",[41,1192,1193],{"class":51}," b.score), [items])\n",[41,1195,1196],{"class":43,"line":254},[41,1197,1198],{"class":72},"  \u002F\u002F If items doesn't change but parent re-renders → memo saves 50ms per render.\n",[41,1200,1201,1203,1205,1207,1209,1211,1213,1215,1217,1219,1222,1224,1226,1229,1231,1233,1236,1238],{"class":43,"line":260},[41,1202,424],{"class":47},[41,1204,427],{"class":51},[41,1206,431],{"class":430},[41,1208,1093],{"class":51},[41,1210,836],{"class":89},[41,1212,410],{"class":51},[41,1214,1100],{"class":108},[41,1216,843],{"class":47},[41,1218,427],{"class":51},[41,1220,1221],{"class":82},"Row",[41,1223,1109],{"class":89},[41,1225,437],{"class":47},[41,1227,1228],{"class":51},"{i.id} ",[41,1230,352],{"class":89},[41,1232,437],{"class":47},[41,1234,1235],{"class":51},"{i} \u002F>)}\u003C\u002F",[41,1237,431],{"class":430},[41,1239,445],{"class":51},[41,1241,1242],{"class":43,"line":767},[41,1243,460],{"class":51},[23,1245,1247],{"id":1246},"common-pitfall-inline-objects-break-memo","Common Pitfall: Inline Objects Break memo",[28,1249,1251],{"filename":1250,"language":31},"inline_object_pitfall.js",[33,1252,1254],{"className":35,"code":1253,"language":31,"meta":37,"style":37},"\u002F\u002F ANTI-PATTERN: React.memo'd child receives a new inline object every render\nconst StyledButton = memo(function StyledButton({ label, style }) {\n  return \u003Cbutton style={style}>{label}\u003C\u002Fbutton>\n})\n\nfunction Toolbar({ theme }) {\n  return \u003CStyledButton\n    label=\"Save\"\n    style={{ color: theme.color, padding: '8px' }}  \u002F\u002F ← NEW OBJECT every render\n  \u002F>\n  \u002F\u002F React.memo compares prevStyle === nextStyle → {} !== {} → ALWAYS re-renders.\n  \u002F\u002F The memo is completely defeated by the inline object.\n}\n\n\u002F\u002F FIX: memoize the style object\nfunction Toolbar({ theme }) {\n  const buttonStyle = useMemo(\n    () => ({ color: theme.color, padding: '8px' }),\n    [theme.color]  \u002F\u002F only re-create when theme.color changes\n  )\n  return \u003CStyledButton label=\"Save\" style={buttonStyle} \u002F>\n  \u002F\u002F Now the style reference is stable → React.memo works.\n}\n\n\u002F\u002F OR: hoist to module scope if truly constant\nconst STATIC_STYLE = { color: 'blue', padding: '8px' }\n",[15,1255,1256,1261,1290,1311,1315,1319,1333,1342,1352,1371,1376,1381,1386,1390,1394,1399,1411,1424,1438,1446,1450,1474,1479,1483,1487,1492],{"__ignoreMap":37},[41,1257,1258],{"class":43,"line":44},[41,1259,1260],{"class":72},"\u002F\u002F ANTI-PATTERN: React.memo'd child receives a new inline object every render\n",[41,1262,1263,1265,1268,1270,1272,1274,1276,1278,1280,1283,1285,1288],{"class":43,"line":62},[41,1264,79],{"class":47},[41,1266,1267],{"class":82}," StyledButton",[41,1269,86],{"class":47},[41,1271,504],{"class":89},[41,1273,410],{"class":51},[41,1275,343],{"class":47},[41,1277,1267],{"class":89},[41,1279,349],{"class":51},[41,1281,1282],{"class":108},"label",[41,1284,112],{"class":51},[41,1286,1287],{"class":108},"style",[41,1289,355],{"class":51},[41,1291,1292,1294,1296,1299,1302,1304,1307,1309],{"class":43,"line":69},[41,1293,424],{"class":47},[41,1295,427],{"class":51},[41,1297,1298],{"class":430},"button",[41,1300,1301],{"class":89}," style",[41,1303,437],{"class":47},[41,1305,1306],{"class":51},"{style}>{label}\u003C\u002F",[41,1308,1298],{"class":430},[41,1310,445],{"class":51},[41,1312,1313],{"class":43,"line":76},[41,1314,644],{"class":51},[41,1316,1317],{"class":43,"line":132},[41,1318,66],{"emptyLinePlaceholder":65},[41,1320,1321,1323,1326,1328,1331],{"class":43,"line":138},[41,1322,343],{"class":47},[41,1324,1325],{"class":89}," Toolbar",[41,1327,349],{"class":51},[41,1329,1330],{"class":108},"theme",[41,1332,355],{"class":51},[41,1334,1335,1337,1339],{"class":43,"line":144},[41,1336,424],{"class":47},[41,1338,427],{"class":51},[41,1340,1341],{"class":82},"StyledButton\n",[41,1343,1344,1347,1349],{"class":43,"line":149},[41,1345,1346],{"class":89},"    label",[41,1348,437],{"class":47},[41,1350,1351],{"class":58},"\"Save\"\n",[41,1353,1354,1357,1359,1362,1365,1368],{"class":43,"line":155},[41,1355,1356],{"class":89},"    style",[41,1358,437],{"class":47},[41,1360,1361],{"class":51},"{{ color: theme.color, padding: ",[41,1363,1364],{"class":58},"'8px'",[41,1366,1367],{"class":51}," }}  ",[41,1369,1370],{"class":72},"\u002F\u002F ← NEW OBJECT every render\n",[41,1372,1373],{"class":43,"line":178},[41,1374,1375],{"class":51},"  \u002F>\n",[41,1377,1378],{"class":43,"line":184},[41,1379,1380],{"class":72},"  \u002F\u002F React.memo compares prevStyle === nextStyle → {} !== {} → ALWAYS re-renders.\n",[41,1382,1383],{"class":43,"line":190},[41,1384,1385],{"class":72},"  \u002F\u002F The memo is completely defeated by the inline object.\n",[41,1387,1388],{"class":43,"line":195},[41,1389,460],{"class":51},[41,1391,1392],{"class":43,"line":201},[41,1393,66],{"emptyLinePlaceholder":65},[41,1395,1396],{"class":43,"line":207},[41,1397,1398],{"class":72},"\u002F\u002F FIX: memoize the style object\n",[41,1400,1401,1403,1405,1407,1409],{"class":43,"line":212},[41,1402,343],{"class":47},[41,1404,1325],{"class":89},[41,1406,349],{"class":51},[41,1408,1330],{"class":108},[41,1410,355],{"class":51},[41,1412,1413,1415,1418,1420,1422],{"class":43,"line":218},[41,1414,360],{"class":47},[41,1416,1417],{"class":82}," buttonStyle",[41,1419,86],{"class":47},[41,1421,90],{"class":89},[41,1423,686],{"class":51},[41,1425,1426,1428,1430,1433,1435],{"class":43,"line":224},[41,1427,691],{"class":51},[41,1429,96],{"class":47},[41,1431,1432],{"class":51}," ({ color: theme.color, padding: ",[41,1434,1364],{"class":58},[41,1436,1437],{"class":51}," }),\n",[41,1439,1440,1443],{"class":43,"line":230},[41,1441,1442],{"class":51},"    [theme.color]  ",[41,1444,1445],{"class":72},"\u002F\u002F only re-create when theme.color changes\n",[41,1447,1448],{"class":43,"line":236},[41,1449,639],{"class":51},[41,1451,1452,1454,1456,1459,1462,1464,1467,1469,1471],{"class":43,"line":242},[41,1453,424],{"class":47},[41,1455,427],{"class":51},[41,1457,1458],{"class":82},"StyledButton",[41,1460,1461],{"class":89}," label",[41,1463,437],{"class":47},[41,1465,1466],{"class":58},"\"Save\"",[41,1468,1301],{"class":89},[41,1470,437],{"class":47},[41,1472,1473],{"class":51},"{buttonStyle} \u002F>\n",[41,1475,1476],{"class":43,"line":248},[41,1477,1478],{"class":72},"  \u002F\u002F Now the style reference is stable → React.memo works.\n",[41,1480,1481],{"class":43,"line":254},[41,1482,460],{"class":51},[41,1484,1485],{"class":43,"line":260},[41,1486,66],{"emptyLinePlaceholder":65},[41,1488,1489],{"class":43,"line":767},[41,1490,1491],{"class":72},"\u002F\u002F OR: hoist to module scope if truly constant\n",[41,1493,1494,1496,1499,1501,1504,1507,1510,1512],{"class":43,"line":783},[41,1495,79],{"class":47},[41,1497,1498],{"class":82}," STATIC_STYLE",[41,1500,86],{"class":47},[41,1502,1503],{"class":51}," { color: ",[41,1505,1506],{"class":58},"'blue'",[41,1508,1509],{"class":51},", padding: ",[41,1511,1364],{"class":58},[41,1513,1514],{"class":51}," }\n",[23,1516,1518],{"id":1517},"tips-tricks","💡 Tips & Tricks",[28,1520,1522],{"filename":1521,"language":31},"tips.js",[33,1523,1525],{"className":35,"code":1524,"language":31,"meta":37,"style":37},"\u002F\u002F [Performance] Measure BEFORE optimizing. Use React DevTools Profiler to find\n\u002F\u002F which components re-render unnecessarily. Don't sprinkle useMemo\u002FuseCallback\n\u002F\u002F \"just in case\" — most components don't need them.\n\n\u002F\u002F [Idiom] useCallback(fn, []) is equivalent to useRef(fn).current if fn doesn't\n\u002F\u002F need any deps — but useCallback is more idiomatic and readable.\n\n\u002F\u002F [Debug] If React.memo doesn't seem to prevent re-renders, check for:\n\u002F\u002F 1. Inline objects: style={{...}}, config={{...}} → new ref every render\n\u002F\u002F 2. Inline functions: onClick={() => ...} → new ref every render\n\u002F\u002F 3. Children prop: \u003CMemo>text\u003C\u002FMemo> → children string is stable, but\n\u002F\u002F    \u003CMemo>\u003CChild\u002F>\u003C\u002FMemo> → new JSX element every render\n\n\u002F\u002F [Performance] For lists, the highest-leverage optimization is usually\n\u002F\u002F virtualization (react-window\u002Freact-virtual), not memoization. Memoization\n\u002F\u002F helps each row avoid unnecessary re-renders; virtualization avoids rendering\n\u002F\u002F rows that aren't visible at all. See Chapter 20.\n\n\u002F\u002F [Idiom] useMemo for expensive computations: sort\u002Ffilter of large datasets,\n\u002F\u002F complex object construction, parsing. Not for arithmetic or small operations.\n",[15,1526,1527,1532,1537,1542,1546,1551,1556,1560,1565,1570,1575,1580,1585,1589,1594,1599,1604,1609,1613,1618],{"__ignoreMap":37},[41,1528,1529],{"class":43,"line":44},[41,1530,1531],{"class":72},"\u002F\u002F [Performance] Measure BEFORE optimizing. Use React DevTools Profiler to find\n",[41,1533,1534],{"class":43,"line":62},[41,1535,1536],{"class":72},"\u002F\u002F which components re-render unnecessarily. Don't sprinkle useMemo\u002FuseCallback\n",[41,1538,1539],{"class":43,"line":69},[41,1540,1541],{"class":72},"\u002F\u002F \"just in case\" — most components don't need them.\n",[41,1543,1544],{"class":43,"line":76},[41,1545,66],{"emptyLinePlaceholder":65},[41,1547,1548],{"class":43,"line":132},[41,1549,1550],{"class":72},"\u002F\u002F [Idiom] useCallback(fn, []) is equivalent to useRef(fn).current if fn doesn't\n",[41,1552,1553],{"class":43,"line":138},[41,1554,1555],{"class":72},"\u002F\u002F need any deps — but useCallback is more idiomatic and readable.\n",[41,1557,1558],{"class":43,"line":144},[41,1559,66],{"emptyLinePlaceholder":65},[41,1561,1562],{"class":43,"line":149},[41,1563,1564],{"class":72},"\u002F\u002F [Debug] If React.memo doesn't seem to prevent re-renders, check for:\n",[41,1566,1567],{"class":43,"line":155},[41,1568,1569],{"class":72},"\u002F\u002F 1. Inline objects: style={{...}}, config={{...}} → new ref every render\n",[41,1571,1572],{"class":43,"line":178},[41,1573,1574],{"class":72},"\u002F\u002F 2. Inline functions: onClick={() => ...} → new ref every render\n",[41,1576,1577],{"class":43,"line":184},[41,1578,1579],{"class":72},"\u002F\u002F 3. Children prop: \u003CMemo>text\u003C\u002FMemo> → children string is stable, but\n",[41,1581,1582],{"class":43,"line":190},[41,1583,1584],{"class":72},"\u002F\u002F    \u003CMemo>\u003CChild\u002F>\u003C\u002FMemo> → new JSX element every render\n",[41,1586,1587],{"class":43,"line":195},[41,1588,66],{"emptyLinePlaceholder":65},[41,1590,1591],{"class":43,"line":201},[41,1592,1593],{"class":72},"\u002F\u002F [Performance] For lists, the highest-leverage optimization is usually\n",[41,1595,1596],{"class":43,"line":207},[41,1597,1598],{"class":72},"\u002F\u002F virtualization (react-window\u002Freact-virtual), not memoization. Memoization\n",[41,1600,1601],{"class":43,"line":212},[41,1602,1603],{"class":72},"\u002F\u002F helps each row avoid unnecessary re-renders; virtualization avoids rendering\n",[41,1605,1606],{"class":43,"line":218},[41,1607,1608],{"class":72},"\u002F\u002F rows that aren't visible at all. See Chapter 20.\n",[41,1610,1611],{"class":43,"line":224},[41,1612,66],{"emptyLinePlaceholder":65},[41,1614,1615],{"class":43,"line":230},[41,1616,1617],{"class":72},"\u002F\u002F [Idiom] useMemo for expensive computations: sort\u002Ffilter of large datasets,\n",[41,1619,1620],{"class":43,"line":236},[41,1621,1622],{"class":72},"\u002F\u002F complex object construction, parsing. Not for arithmetic or small operations.\n",[23,1624,1626],{"id":1625},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[28,1628,1630],{"filename":1629,"language":31},"edge_cases.js",[33,1631,1633],{"className":35,"code":1632,"language":31,"meta":37,"style":37},"\u002F\u002F [Gotcha] React.memo does a SHALLOW comparison — Object.is on each prop.\n\u002F\u002F It does NOT deeply compare objects. { a: 1 } !== { a: 1 } → memo fails.\n\u002F\u002F You must stabilize references with useMemo\u002FuseCallback for memo to work.\n\n\u002F\u002F [Gotcha] React.memo with a custom comparison function:\n\u002F\u002F memo(Component, (prevProps, nextProps) => prevProps.data.id === nextProps.data.id)\n\u002F\u002F Returning true = props are equal → skip re-render. Returning false = re-render.\n\u002F\u002F This is the OPPOSITE of what you might expect (true = skip, not true = render).\n\n\u002F\u002F [Gotcha] useMemo doesn't guarantee the value won't be recomputed — React may\n\u002F\u002F \"forget\" memoized values to free memory. Don't rely on useMemo for side effects\n\u002F\u002F or as a guarantee that a computation runs exactly once. Use useRef + useState\n\u002F\u002F for \"run once\" semantics.\n\n\u002F\u002F [Gotcha] useCallback with a function that closes over state will capture the\n\u002F\u002F state at the time the callback was created. If the state changes but isn't in\n\u002F\u002F deps, the callback is stale. Always include all referenced state\u002Fprops in deps.\n\n\u002F\u002F [Gotcha] Children passed as JSX (\u003CMemo>\u003CChild\u002F>\u003C\u002FMemo>) create a new element\n\u002F\u002F every render, which can defeat React.memo if children is a prop. Use\n\u002F\u002F useMemo for the children element or render children outside the memo'd boundary.\n",[15,1634,1635,1640,1645,1650,1654,1659,1664,1669,1674,1678,1683,1688,1693,1698,1702,1707,1712,1717,1721,1726,1731],{"__ignoreMap":37},[41,1636,1637],{"class":43,"line":44},[41,1638,1639],{"class":72},"\u002F\u002F [Gotcha] React.memo does a SHALLOW comparison — Object.is on each prop.\n",[41,1641,1642],{"class":43,"line":62},[41,1643,1644],{"class":72},"\u002F\u002F It does NOT deeply compare objects. { a: 1 } !== { a: 1 } → memo fails.\n",[41,1646,1647],{"class":43,"line":69},[41,1648,1649],{"class":72},"\u002F\u002F You must stabilize references with useMemo\u002FuseCallback for memo to work.\n",[41,1651,1652],{"class":43,"line":76},[41,1653,66],{"emptyLinePlaceholder":65},[41,1655,1656],{"class":43,"line":132},[41,1657,1658],{"class":72},"\u002F\u002F [Gotcha] React.memo with a custom comparison function:\n",[41,1660,1661],{"class":43,"line":138},[41,1662,1663],{"class":72},"\u002F\u002F memo(Component, (prevProps, nextProps) => prevProps.data.id === nextProps.data.id)\n",[41,1665,1666],{"class":43,"line":144},[41,1667,1668],{"class":72},"\u002F\u002F Returning true = props are equal → skip re-render. Returning false = re-render.\n",[41,1670,1671],{"class":43,"line":149},[41,1672,1673],{"class":72},"\u002F\u002F This is the OPPOSITE of what you might expect (true = skip, not true = render).\n",[41,1675,1676],{"class":43,"line":155},[41,1677,66],{"emptyLinePlaceholder":65},[41,1679,1680],{"class":43,"line":178},[41,1681,1682],{"class":72},"\u002F\u002F [Gotcha] useMemo doesn't guarantee the value won't be recomputed — React may\n",[41,1684,1685],{"class":43,"line":184},[41,1686,1687],{"class":72},"\u002F\u002F \"forget\" memoized values to free memory. Don't rely on useMemo for side effects\n",[41,1689,1690],{"class":43,"line":190},[41,1691,1692],{"class":72},"\u002F\u002F or as a guarantee that a computation runs exactly once. Use useRef + useState\n",[41,1694,1695],{"class":43,"line":195},[41,1696,1697],{"class":72},"\u002F\u002F for \"run once\" semantics.\n",[41,1699,1700],{"class":43,"line":201},[41,1701,66],{"emptyLinePlaceholder":65},[41,1703,1704],{"class":43,"line":207},[41,1705,1706],{"class":72},"\u002F\u002F [Gotcha] useCallback with a function that closes over state will capture the\n",[41,1708,1709],{"class":43,"line":212},[41,1710,1711],{"class":72},"\u002F\u002F state at the time the callback was created. If the state changes but isn't in\n",[41,1713,1714],{"class":43,"line":218},[41,1715,1716],{"class":72},"\u002F\u002F deps, the callback is stale. Always include all referenced state\u002Fprops in deps.\n",[41,1718,1719],{"class":43,"line":224},[41,1720,66],{"emptyLinePlaceholder":65},[41,1722,1723],{"class":43,"line":230},[41,1724,1725],{"class":72},"\u002F\u002F [Gotcha] Children passed as JSX (\u003CMemo>\u003CChild\u002F>\u003C\u002FMemo>) create a new element\n",[41,1727,1728],{"class":43,"line":236},[41,1729,1730],{"class":72},"\u002F\u002F every render, which can defeat React.memo if children is a prop. Use\n",[41,1732,1733],{"class":43,"line":242},[41,1734,1735],{"class":72},"\u002F\u002F useMemo for the children element or render children outside the memo'd boundary.\n",[23,1737,1739],{"id":1738},"spot-the-bug","🧠 Spot the Bug",[1741,1742,1743,1744,1747],"p",{},"A ",[15,1745,1746],{},"React.memo","'d child component re-renders on every parent render, even though the developer expected it to be stable:",[28,1749,1751],{"filename":1750,"language":31},"spot_the_bug.js",[33,1752,1754],{"className":35,"code":1753,"language":31,"meta":37,"style":37},"const ExpensiveChart = memo(function ExpensiveChart({ data, options, onSelect }) {\n  return \u003CChart data={data} options={options} onSelect={onSelect} \u002F>\n})\n\nfunction Dashboard({ rawData, theme }) {\n  return (\n    \u003CExpensiveChart\n      data={rawData}\n      options={{ theme, animated: true }}  \u002F\u002F ← new object every render\n      onSelect={(id) => console.log(id)}   \u002F\u002F ← new function every render\n    \u002F>\n  )\n}\n",[15,1755,1756,1788,1819,1823,1827,1845,1851,1858,1868,1886,1912,1917,1921],{"__ignoreMap":37},[41,1757,1758,1760,1763,1765,1767,1769,1771,1773,1775,1777,1779,1782,1784,1786],{"class":43,"line":44},[41,1759,79],{"class":47},[41,1761,1762],{"class":82}," ExpensiveChart",[41,1764,86],{"class":47},[41,1766,504],{"class":89},[41,1768,410],{"class":51},[41,1770,343],{"class":47},[41,1772,1762],{"class":89},[41,1774,349],{"class":51},[41,1776,352],{"class":108},[41,1778,112],{"class":51},[41,1780,1781],{"class":108},"options",[41,1783,112],{"class":51},[41,1785,521],{"class":108},[41,1787,355],{"class":51},[41,1789,1790,1792,1794,1797,1800,1802,1805,1807,1809,1812,1814,1816],{"class":43,"line":62},[41,1791,424],{"class":47},[41,1793,427],{"class":51},[41,1795,1796],{"class":82},"Chart",[41,1798,1799],{"class":89}," data",[41,1801,437],{"class":47},[41,1803,1804],{"class":51},"{data} ",[41,1806,1781],{"class":89},[41,1808,437],{"class":47},[41,1810,1811],{"class":51},"{options} ",[41,1813,521],{"class":89},[41,1815,437],{"class":47},[41,1817,1818],{"class":51},"{onSelect} \u002F>\n",[41,1820,1821],{"class":43,"line":69},[41,1822,644],{"class":51},[41,1824,1825],{"class":43,"line":76},[41,1826,66],{"emptyLinePlaceholder":65},[41,1828,1829,1831,1834,1836,1839,1841,1843],{"class":43,"line":132},[41,1830,343],{"class":47},[41,1832,1833],{"class":89}," Dashboard",[41,1835,349],{"class":51},[41,1837,1838],{"class":108},"rawData",[41,1840,112],{"class":51},[41,1842,1330],{"class":108},[41,1844,355],{"class":51},[41,1846,1847,1849],{"class":43,"line":138},[41,1848,424],{"class":47},[41,1850,550],{"class":51},[41,1852,1853,1855],{"class":43,"line":144},[41,1854,555],{"class":51},[41,1856,1857],{"class":82},"ExpensiveChart\n",[41,1859,1860,1863,1865],{"class":43,"line":149},[41,1861,1862],{"class":89},"      data",[41,1864,437],{"class":47},[41,1866,1867],{"class":51},"{rawData}\n",[41,1869,1870,1873,1875,1878,1881,1883],{"class":43,"line":155},[41,1871,1872],{"class":89},"      options",[41,1874,437],{"class":47},[41,1876,1877],{"class":51},"{{ theme, animated: ",[41,1879,1880],{"class":82},"true",[41,1882,1367],{"class":51},[41,1884,1885],{"class":72},"\u002F\u002F ← new object every render\n",[41,1887,1888,1891,1893,1896,1898,1900,1902,1904,1906,1909],{"class":43,"line":178},[41,1889,1890],{"class":89},"      onSelect",[41,1892,437],{"class":47},[41,1894,1895],{"class":51},"{(",[41,1897,757],{"class":108},[41,1899,118],{"class":51},[41,1901,96],{"class":47},[41,1903,404],{"class":51},[41,1905,407],{"class":89},[41,1907,1908],{"class":51},"(id)}   ",[41,1910,1911],{"class":72},"\u002F\u002F ← new function every render\n",[41,1913,1914],{"class":43,"line":184},[41,1915,1916],{"class":51},"    \u002F>\n",[41,1918,1919],{"class":43,"line":190},[41,1920,639],{"class":51},[41,1922,1923],{"class":43,"line":195},[41,1924,460],{"class":51},[1926,1927,1928,1932,1960,1966,2072],"details",{},[1929,1930,1931],"summary",{},"Answer",[1741,1933,1934,1935,1937,1938,1940,1941,1943,1944,1947,1948,1937,1951,1954,1955,1959],{},"Both ",[15,1936,1781],{}," and ",[15,1939,521],{}," are inline literals — new object\u002Ffunction references on every render. ",[15,1942,1746],{}," does a shallow comparison (",[15,1945,1946],{},"Object.is"," on each prop), and since ",[15,1949,1950],{},"{ theme, animated: true } !== { theme, animated: true }",[15,1952,1953],{},"() => console.log(id) !== () => console.log(id)",", the memo ",[1956,1957,1958],"strong",{},"always"," fails and the chart re-renders every time.",[1741,1961,1962,1965],{},[1956,1963,1964],{},"Fix",":",[33,1967,1969],{"className":35,"code":1968,"language":31,"meta":37,"style":37},"function Dashboard({ rawData, theme }) {\n  const options = useMemo(() => ({ theme, animated: true }), [theme])\n  const handleSelect = useCallback((id) => console.log(id), [])\n\n  return \u003CExpensiveChart data={rawData} options={options} onSelect={handleSelect} \u002F>\n}\n",[15,1970,1971,1987,2010,2035,2039,2068],{"__ignoreMap":37},[41,1972,1973,1975,1977,1979,1981,1983,1985],{"class":43,"line":44},[41,1974,343],{"class":47},[41,1976,1833],{"class":89},[41,1978,349],{"class":51},[41,1980,1838],{"class":108},[41,1982,112],{"class":51},[41,1984,1330],{"class":108},[41,1986,355],{"class":51},[41,1988,1989,1991,1994,1996,1998,2000,2002,2005,2007],{"class":43,"line":62},[41,1990,360],{"class":47},[41,1992,1993],{"class":82}," options",[41,1995,86],{"class":47},[41,1997,90],{"class":89},[41,1999,93],{"class":51},[41,2001,96],{"class":47},[41,2003,2004],{"class":51}," ({ theme, animated: ",[41,2006,1880],{"class":82},[41,2008,2009],{"class":51}," }), [theme])\n",[41,2011,2012,2014,2016,2018,2020,2022,2024,2026,2028,2030,2032],{"class":43,"line":69},[41,2013,360],{"class":47},[41,2015,748],{"class":82},[41,2017,86],{"class":47},[41,2019,165],{"class":89},[41,2021,105],{"class":51},[41,2023,757],{"class":108},[41,2025,118],{"class":51},[41,2027,96],{"class":47},[41,2029,404],{"class":51},[41,2031,407],{"class":89},[41,2033,2034],{"class":51},"(id), [])\n",[41,2036,2037],{"class":43,"line":76},[41,2038,66],{"emptyLinePlaceholder":65},[41,2040,2041,2043,2045,2048,2050,2052,2055,2057,2059,2061,2063,2065],{"class":43,"line":132},[41,2042,424],{"class":47},[41,2044,427],{"class":51},[41,2046,2047],{"class":82},"ExpensiveChart",[41,2049,1799],{"class":89},[41,2051,437],{"class":47},[41,2053,2054],{"class":51},"{rawData} ",[41,2056,1781],{"class":89},[41,2058,437],{"class":47},[41,2060,1811],{"class":51},[41,2062,521],{"class":89},[41,2064,437],{"class":47},[41,2066,2067],{"class":51},"{handleSelect} \u002F>\n",[41,2069,2070],{"class":43,"line":138},[41,2071,460],{"class":51},[1741,2073,2074,2075,2077,2078,2080,2081,2084,2085,2087,2088,2090,2091,2093],{},"Now ",[15,2076,1781],{}," only changes when ",[15,2079,1330],{}," changes, and ",[15,2082,2083],{},"handleSelect"," is stable forever — ",[15,2086,1746],{}," succeeds and the chart only re-renders when ",[15,2089,1838],{}," or ",[15,2092,1330],{}," actually changes.",[23,2095,2097],{"id":2096},"key-takeaways","Key Takeaways",[28,2099,2101],{"filename":2100,"language":31},"key_takeaways.js",[33,2102,2104],{"className":35,"code":2103,"language":31,"meta":37,"style":37},"\u002F\u002F 1. useMemo memoizes a VALUE, useCallback memoizes a FUNCTION reference.\n\u002F\u002F    Both return the same cached result if deps haven't changed (Object.is).\n\u002F\u002F    useCallback(fn, deps) === useMemo(() => fn, deps).\n\n\u002F\u002F 2. They exist for REFERENTIAL EQUALITY — preventing new object\u002Ffunction refs\n\u002F\u002F    from breaking React.memo, dependency arrays, and context value comparison.\n\n\u002F\u002F 3. Use when: value\u002Ffunction goes to a React.memo'd child, is in a dep array,\n\u002F\u002F    is an expensive computation, or is a context value.\n\u002F\u002F    Don't use when: computation is cheap, child isn't memo'd, no dep array needs it.\n\n\u002F\u002F 4. React.memo + useMemo + useCallback must ALL be present for the optimization\n\u002F\u002F    to work. Missing any one: memo on child (no memo → always re-renders),\n\u002F\u002F    unstable data ref, unstable handler ref → memo comparison fails.\n\n\u002F\u002F 5. Memoization has overhead (comparison + storage). Measure with the Profiler\n\u002F\u002F    before optimizing. For lists, virtualization (Ch 20) usually beats memoization.\n",[15,2105,2106,2111,2116,2121,2125,2130,2135,2139,2144,2149,2154,2158,2163,2168,2173,2177,2182],{"__ignoreMap":37},[41,2107,2108],{"class":43,"line":44},[41,2109,2110],{"class":72},"\u002F\u002F 1. useMemo memoizes a VALUE, useCallback memoizes a FUNCTION reference.\n",[41,2112,2113],{"class":43,"line":62},[41,2114,2115],{"class":72},"\u002F\u002F    Both return the same cached result if deps haven't changed (Object.is).\n",[41,2117,2118],{"class":43,"line":69},[41,2119,2120],{"class":72},"\u002F\u002F    useCallback(fn, deps) === useMemo(() => fn, deps).\n",[41,2122,2123],{"class":43,"line":76},[41,2124,66],{"emptyLinePlaceholder":65},[41,2126,2127],{"class":43,"line":132},[41,2128,2129],{"class":72},"\u002F\u002F 2. They exist for REFERENTIAL EQUALITY — preventing new object\u002Ffunction refs\n",[41,2131,2132],{"class":43,"line":138},[41,2133,2134],{"class":72},"\u002F\u002F    from breaking React.memo, dependency arrays, and context value comparison.\n",[41,2136,2137],{"class":43,"line":144},[41,2138,66],{"emptyLinePlaceholder":65},[41,2140,2141],{"class":43,"line":149},[41,2142,2143],{"class":72},"\u002F\u002F 3. Use when: value\u002Ffunction goes to a React.memo'd child, is in a dep array,\n",[41,2145,2146],{"class":43,"line":155},[41,2147,2148],{"class":72},"\u002F\u002F    is an expensive computation, or is a context value.\n",[41,2150,2151],{"class":43,"line":178},[41,2152,2153],{"class":72},"\u002F\u002F    Don't use when: computation is cheap, child isn't memo'd, no dep array needs it.\n",[41,2155,2156],{"class":43,"line":184},[41,2157,66],{"emptyLinePlaceholder":65},[41,2159,2160],{"class":43,"line":190},[41,2161,2162],{"class":72},"\u002F\u002F 4. React.memo + useMemo + useCallback must ALL be present for the optimization\n",[41,2164,2165],{"class":43,"line":195},[41,2166,2167],{"class":72},"\u002F\u002F    to work. Missing any one: memo on child (no memo → always re-renders),\n",[41,2169,2170],{"class":43,"line":201},[41,2171,2172],{"class":72},"\u002F\u002F    unstable data ref, unstable handler ref → memo comparison fails.\n",[41,2174,2175],{"class":43,"line":207},[41,2176,66],{"emptyLinePlaceholder":65},[41,2178,2179],{"class":43,"line":212},[41,2180,2181],{"class":72},"\u002F\u002F 5. Memoization has overhead (comparison + storage). Measure with the Profiler\n",[41,2183,2184],{"class":43,"line":218},[41,2185,2186],{"class":72},"\u002F\u002F    before optimizing. For lists, virtualization (Ch 20) usually beats memoization.\n",[1287,2188,2189],{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}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 .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}",{"title":37,"searchDepth":62,"depth":62,"links":2191},[2192,2193,2194,2195,2196,2197,2198,2199,2200],{"id":25,"depth":62,"text":26},{"id":266,"depth":62,"text":267},{"id":463,"depth":62,"text":464},{"id":972,"depth":62,"text":973},{"id":1246,"depth":62,"text":1247},{"id":1517,"depth":62,"text":1518},{"id":1625,"depth":62,"text":1626},{"id":1738,"depth":62,"text":1739},{"id":2096,"depth":62,"text":2097},"Memoization mechanics, referential equality, React.memo integration, when memoization helps vs hurts, and production patterns for optimized lists. Code-first reference for mid-to-senior React engineers.","md",{},"\u002Freact\u002F09-usememo-and-usecallback",{"title":5,"description":2201},"react\u002F09-usememo-and-usecallback","Mm8rSzP321_igSW_OkdZbsA3y65lriV3ls5PICu_cO8",1789924652145]