[{"data":1,"prerenderedAt":1315},["ShallowReactive",2],{"blog-\u002Fblog\u002Freact-usecallback-usememo-guide":3},{"id":4,"title":5,"body":6,"description":1300,"difficulty":1301,"extension":1302,"framework":1303,"frameworkSlug":1304,"meta":1305,"navigation":593,"order":132,"path":1306,"qaPath":1307,"seo":1308,"stem":1309,"subtopic":1310,"topic":1311,"topicSlug":1312,"updated":1313,"__hash__":1314},"blog\u002Fblog\u002Freact-usecallback-usememo-guide.md","React useCallback & useMemo — Complete Interview Guide",{"type":7,"value":8,"toc":1286},"minimark",[9,14,23,136,151,154,168,217,228,231,241,322,332,336,345,399,408,412,419,475,484,488,497,726,737,741,748,874,881,885,891,956,959,963,971,974,990,997,1058,1065,1091,1095,1102,1181,1191,1195,1198,1227,1233,1237,1282],[10,11,13],"h2",{"id":12},"the-problem-these-hooks-solve","The problem these hooks solve",[15,16,17,18,22],"p",{},"Every render of a React component runs the function body from top to bottom. Every\nfunction and object defined inside that body is a ",[19,20,21],"strong",{},"brand new reference"," each time.",[24,25,30],"pre",{"className":26,"code":27,"language":28,"meta":29,"style":29},"language-jsx shiki shiki-themes github-light github-dark","function Parent() {\n  const options = { color: 'blue' }  \u002F\u002F new object every render\n  const handleClick = () => save()   \u002F\u002F new function every render\n  return \u003CChild options={options} onClick={handleClick} \u002F>\n}\n","jsx","",[31,32,33,50,77,102,130],"code",{"__ignoreMap":29},[34,35,38,42,46],"span",{"class":36,"line":37},"line",1,[34,39,41],{"class":40},"szBVR","function",[34,43,45],{"class":44},"sScJk"," Parent",[34,47,49],{"class":48},"sVt8B","() {\n",[34,51,53,56,60,63,66,70,73],{"class":36,"line":52},2,[34,54,55],{"class":40},"  const",[34,57,59],{"class":58},"sj4cs"," options",[34,61,62],{"class":40}," =",[34,64,65],{"class":48}," { color: ",[34,67,69],{"class":68},"sZZnC","'blue'",[34,71,72],{"class":48}," }  ",[34,74,76],{"class":75},"sJ8bj","\u002F\u002F new object every render\n",[34,78,80,82,85,87,90,93,96,99],{"class":36,"line":79},3,[34,81,55],{"class":40},[34,83,84],{"class":44}," handleClick",[34,86,62],{"class":40},[34,88,89],{"class":48}," () ",[34,91,92],{"class":40},"=>",[34,94,95],{"class":44}," save",[34,97,98],{"class":48},"()   ",[34,100,101],{"class":75},"\u002F\u002F new function every render\n",[34,103,105,108,111,114,116,119,122,125,127],{"class":36,"line":104},4,[34,106,107],{"class":40},"  return",[34,109,110],{"class":48}," \u003C",[34,112,113],{"class":58},"Child",[34,115,59],{"class":44},[34,117,118],{"class":40},"=",[34,120,121],{"class":48},"{options} ",[34,123,124],{"class":44},"onClick",[34,126,118],{"class":40},[34,128,129],{"class":48},"{handleClick} \u002F>\n",[34,131,133],{"class":36,"line":132},5,[34,134,135],{"class":48},"}\n",[15,137,138,139,142,143,146,147,150],{},"Most of the time this is fine — React's reconciler is fast, and a new reference doesn't\ncause visible slowness. But it breaks two specific optimization mechanisms: ",[31,140,141],{},"React.memo","\nand effect dependency arrays. ",[31,144,145],{},"useCallback"," and ",[31,148,149],{},"useMemo"," solve exactly this by giving\nyou stable references that only change when their dependencies change.",[10,152,145],{"id":153},"usecallback",[15,155,156,159,160,163,164,167],{},[31,157,158],{},"useCallback(fn, deps)"," returns a memoized function. On each render it compares ",[31,161,162],{},"deps","\nwith the previous render's deps using ",[31,165,166],{},"Object.is",". If nothing changed, it returns the\nexact same function reference from last time.",[24,169,171],{"className":26,"code":170,"language":28,"meta":29,"style":29},"const handleSort = useCallback((column) => {\n  setSort(column)\n}, []) \u002F\u002F deps: empty — sort setter is always the same\n",[31,172,173,201,209],{"__ignoreMap":29},[34,174,175,178,181,183,186,189,193,196,198],{"class":36,"line":37},[34,176,177],{"class":40},"const",[34,179,180],{"class":58}," handleSort",[34,182,62],{"class":40},[34,184,185],{"class":44}," useCallback",[34,187,188],{"class":48},"((",[34,190,192],{"class":191},"s4XuR","column",[34,194,195],{"class":48},") ",[34,197,92],{"class":40},[34,199,200],{"class":48}," {\n",[34,202,203,206],{"class":36,"line":52},[34,204,205],{"class":44},"  setSort",[34,207,208],{"class":48},"(column)\n",[34,210,211,214],{"class":36,"line":79},[34,212,213],{"class":48},"}, []) ",[34,215,216],{"class":75},"\u002F\u002F deps: empty — sort setter is always the same\n",[15,218,219,220,223,224,227],{},"This is purely about ",[19,221,222],{},"referential stability"," — the function still runs normally when\ncalled. The optimization is that ",[31,225,226],{},"handleSort === previousHandleSort"," between renders\nwhere the deps didn't change.",[10,229,149],{"id":230},"usememo",[15,232,233,236,237,240],{},[31,234,235],{},"useMemo(() => computation, deps)"," caches the ",[19,238,239],{},"return value"," of a computation. It only\nre-runs the computation when a dependency changes.",[24,242,244],{"className":26,"code":243,"language":28,"meta":29,"style":29},"const filtered = useMemo(\n  () => products.filter(p => p.active).sort((a, b) => a.price - b.price),\n  [products]\n)\n",[31,245,246,261,312,317],{"__ignoreMap":29},[34,247,248,250,253,255,258],{"class":36,"line":37},[34,249,177],{"class":40},[34,251,252],{"class":58}," filtered",[34,254,62],{"class":40},[34,256,257],{"class":44}," useMemo",[34,259,260],{"class":48},"(\n",[34,262,263,266,268,271,274,277,279,282,285,288,290,293,296,299,301,303,306,309],{"class":36,"line":52},[34,264,265],{"class":48},"  () ",[34,267,92],{"class":40},[34,269,270],{"class":48}," products.",[34,272,273],{"class":44},"filter",[34,275,276],{"class":48},"(",[34,278,15],{"class":191},[34,280,281],{"class":40}," =>",[34,283,284],{"class":48}," p.active).",[34,286,287],{"class":44},"sort",[34,289,188],{"class":48},[34,291,292],{"class":191},"a",[34,294,295],{"class":48},", ",[34,297,298],{"class":191},"b",[34,300,195],{"class":48},[34,302,92],{"class":40},[34,304,305],{"class":48}," a.price ",[34,307,308],{"class":40},"-",[34,310,311],{"class":48}," b.price),\n",[34,313,314],{"class":36,"line":79},[34,315,316],{"class":48},"  [products]\n",[34,318,319],{"class":36,"line":104},[34,320,321],{"class":48},")\n",[15,323,324,325,327,328,331],{},"Without ",[31,326,149],{},", the filter and sort run on every render — even on a keystroke that\nupdates completely unrelated state. With it, the sorted result is reused until ",[31,329,330],{},"products","\nchanges.",[10,333,335],{"id":334},"the-relationship-between-the-two","The relationship between the two",[15,337,338,340,341,344],{},[31,339,158],{}," is exactly ",[31,342,343],{},"useMemo(() => fn, deps)",". They're the same mechanism:\none is specialized for the \"the value is a function\" case.",[24,346,348],{"className":26,"code":347,"language":28,"meta":29,"style":29},"const fn = useCallback(() => compute(x), [x])\n\u002F\u002F identical to:\nconst fn = useMemo(() => () => compute(x), [x])\n",[31,349,350,372,377],{"__ignoreMap":29},[34,351,352,354,357,359,361,364,366,369],{"class":36,"line":37},[34,353,177],{"class":40},[34,355,356],{"class":58}," fn",[34,358,62],{"class":40},[34,360,185],{"class":44},[34,362,363],{"class":48},"(() ",[34,365,92],{"class":40},[34,367,368],{"class":44}," compute",[34,370,371],{"class":48},"(x), [x])\n",[34,373,374],{"class":36,"line":52},[34,375,376],{"class":75},"\u002F\u002F identical to:\n",[34,378,379,381,383,385,387,389,391,393,395,397],{"class":36,"line":79},[34,380,177],{"class":40},[34,382,356],{"class":58},[34,384,62],{"class":40},[34,386,257],{"class":44},[34,388,363],{"class":48},[34,390,92],{"class":40},[34,392,89],{"class":48},[34,394,92],{"class":40},[34,396,368],{"class":44},[34,398,371],{"class":48},[15,400,401,402,404,405,407],{},"In practice, prefer the purpose-fit name: ",[31,403,145],{}," for stable function references,\n",[31,406,149],{}," for stable computed values.",[10,409,411],{"id":410},"why-referential-equality-matters","Why referential equality matters",[15,413,414,415,418],{},"JavaScript compares objects and functions by ",[19,416,417],{},"reference"," (memory address), not by\ncontent. Two identically written functions are never equal to each other:",[24,420,422],{"className":26,"code":421,"language":28,"meta":29,"style":29},"(() => {}) === (() => {})  \u002F\u002F false — different objects in memory\nconst fn = () => {}\nfn === fn                  \u002F\u002F true — same reference\n",[31,423,424,447,462],{"__ignoreMap":29},[34,425,426,428,430,433,436,439,441,444],{"class":36,"line":37},[34,427,363],{"class":48},[34,429,92],{"class":40},[34,431,432],{"class":48}," {}) ",[34,434,435],{"class":40},"===",[34,437,438],{"class":48}," (() ",[34,440,92],{"class":40},[34,442,443],{"class":48}," {})  ",[34,445,446],{"class":75},"\u002F\u002F false — different objects in memory\n",[34,448,449,451,453,455,457,459],{"class":36,"line":52},[34,450,177],{"class":40},[34,452,356],{"class":44},[34,454,62],{"class":40},[34,456,89],{"class":48},[34,458,92],{"class":40},[34,460,461],{"class":48}," {}\n",[34,463,464,467,469,472],{"class":36,"line":79},[34,465,466],{"class":48},"fn ",[34,468,435],{"class":40},[34,470,471],{"class":48}," fn                  ",[34,473,474],{"class":75},"\u002F\u002F true — same reference\n",[15,476,477,478,480,481,483],{},"This matters because ",[31,479,141],{}," bails out of a re-render only when every prop is\n",[31,482,166],{},"-equal to the previous render. An inline function prop fails that check on\nevery render, defeating the memo.",[10,485,487],{"id":486},"combining-reactmemo-with-usecallback","Combining React.memo with useCallback",[15,489,490,491,493,494,496],{},"The pattern that actually works: ",[31,492,141],{}," on the child component, ",[31,495,145],{}," for\nthe function props passed from the parent.",[24,498,500],{"className":26,"code":499,"language":28,"meta":29,"style":29},"\u002F\u002F Child: skip re-renders when props are equal\nconst Row = React.memo(function Row({ item, onDelete }) {\n  return \u003Cdiv>{item.name} \u003Cbutton onClick={() => onDelete(item.id)}>x\u003C\u002Fbutton>\u003C\u002Fdiv>\n})\n\n\u002F\u002F Parent: stable function reference so memo can work\nfunction List({ items }) {\n  const handleDelete = useCallback(id => {\n    setItems(prev => prev.filter(i => i.id !== id))\n  }, [])\n  return items.map(item => \u003CRow key={item.id} item={item} onDelete={handleDelete} \u002F>)\n}\n",[31,501,502,507,542,584,589,595,601,616,637,671,677,721],{"__ignoreMap":29},[34,503,504],{"class":36,"line":37},[34,505,506],{"class":75},"\u002F\u002F Child: skip re-renders when props are equal\n",[34,508,509,511,514,516,519,522,524,526,528,531,534,536,539],{"class":36,"line":52},[34,510,177],{"class":40},[34,512,513],{"class":58}," Row",[34,515,62],{"class":40},[34,517,518],{"class":48}," React.",[34,520,521],{"class":44},"memo",[34,523,276],{"class":48},[34,525,41],{"class":40},[34,527,513],{"class":44},[34,529,530],{"class":48},"({ ",[34,532,533],{"class":191},"item",[34,535,295],{"class":48},[34,537,538],{"class":191},"onDelete",[34,540,541],{"class":48}," }) {\n",[34,543,544,546,548,552,555,558,561,563,566,568,571,574,576,579,581],{"class":36,"line":79},[34,545,107],{"class":40},[34,547,110],{"class":48},[34,549,551],{"class":550},"s9eBZ","div",[34,553,554],{"class":48},">{item.name} \u003C",[34,556,557],{"class":550},"button",[34,559,560],{"class":44}," onClick",[34,562,118],{"class":40},[34,564,565],{"class":48},"{() ",[34,567,92],{"class":40},[34,569,570],{"class":44}," onDelete",[34,572,573],{"class":48},"(item.id)}>x\u003C\u002F",[34,575,557],{"class":550},[34,577,578],{"class":48},">\u003C\u002F",[34,580,551],{"class":550},[34,582,583],{"class":48},">\n",[34,585,586],{"class":36,"line":104},[34,587,588],{"class":48},"})\n",[34,590,591],{"class":36,"line":132},[34,592,594],{"emptyLinePlaceholder":593},true,"\n",[34,596,598],{"class":36,"line":597},6,[34,599,600],{"class":75},"\u002F\u002F Parent: stable function reference so memo can work\n",[34,602,604,606,609,611,614],{"class":36,"line":603},7,[34,605,41],{"class":40},[34,607,608],{"class":44}," List",[34,610,530],{"class":48},[34,612,613],{"class":191},"items",[34,615,541],{"class":48},[34,617,619,621,624,626,628,630,633,635],{"class":36,"line":618},8,[34,620,55],{"class":40},[34,622,623],{"class":58}," handleDelete",[34,625,62],{"class":40},[34,627,185],{"class":44},[34,629,276],{"class":48},[34,631,632],{"class":191},"id",[34,634,281],{"class":40},[34,636,200],{"class":48},[34,638,640,643,645,648,650,653,655,657,660,662,665,668],{"class":36,"line":639},9,[34,641,642],{"class":44},"    setItems",[34,644,276],{"class":48},[34,646,647],{"class":191},"prev",[34,649,281],{"class":40},[34,651,652],{"class":48}," prev.",[34,654,273],{"class":44},[34,656,276],{"class":48},[34,658,659],{"class":191},"i",[34,661,281],{"class":40},[34,663,664],{"class":48}," i.id ",[34,666,667],{"class":40},"!==",[34,669,670],{"class":48}," id))\n",[34,672,674],{"class":36,"line":673},10,[34,675,676],{"class":48},"  }, [])\n",[34,678,680,682,685,688,690,692,694,696,699,702,704,707,709,711,714,716,718],{"class":36,"line":679},11,[34,681,107],{"class":40},[34,683,684],{"class":48}," items.",[34,686,687],{"class":44},"map",[34,689,276],{"class":48},[34,691,533],{"class":191},[34,693,281],{"class":40},[34,695,110],{"class":48},[34,697,698],{"class":58},"Row",[34,700,701],{"class":44}," key",[34,703,118],{"class":40},[34,705,706],{"class":48},"{item.id} ",[34,708,533],{"class":44},[34,710,118],{"class":40},[34,712,713],{"class":48},"{item} ",[34,715,538],{"class":44},[34,717,118],{"class":40},[34,719,720],{"class":48},"{handleDelete} \u002F>)\n",[34,722,724],{"class":36,"line":723},12,[34,725,135],{"class":48},[15,727,324,728,295,730,733,734,736],{},[31,729,145],{},[31,731,732],{},"handleDelete"," is a new function each render → ",[31,735,698],{},"'s memo\nnever hits → every item re-renders on any list change.",[10,738,740],{"id":739},"stabilizing-effect-dependencies","Stabilizing effect dependencies",[15,742,743,744,747],{},"Functions defined inside a component are new references every render. If an effect\ndepends on such a function, it re-runs on every render — the very problem ",[31,745,746],{},"useEffect","\ndeps are supposed to prevent.",[24,749,751],{"className":26,"code":750,"language":28,"meta":29,"style":29},"\u002F\u002F without useCallback: refetch fires every render\nfunction Page({ userId }) {\n  const fetchUser = () => api.get(userId)\n  useEffect(() => { fetchUser().then(setUser) }, [fetchUser]) \u002F\u002F new fn each render\n}\n\n\u002F\u002F with useCallback: refetch fires only when userId changes\nconst fetchUser = useCallback(() => api.get(userId), [userId])\nuseEffect(() => { fetchUser().then(setUser) }, [fetchUser])\n",[31,752,753,758,772,794,821,825,829,834,855],{"__ignoreMap":29},[34,754,755],{"class":36,"line":37},[34,756,757],{"class":75},"\u002F\u002F without useCallback: refetch fires every render\n",[34,759,760,762,765,767,770],{"class":36,"line":52},[34,761,41],{"class":40},[34,763,764],{"class":44}," Page",[34,766,530],{"class":48},[34,768,769],{"class":191},"userId",[34,771,541],{"class":48},[34,773,774,776,779,781,783,785,788,791],{"class":36,"line":79},[34,775,55],{"class":40},[34,777,778],{"class":44}," fetchUser",[34,780,62],{"class":40},[34,782,89],{"class":48},[34,784,92],{"class":40},[34,786,787],{"class":48}," api.",[34,789,790],{"class":44},"get",[34,792,793],{"class":48},"(userId)\n",[34,795,796,799,801,803,806,809,812,815,818],{"class":36,"line":104},[34,797,798],{"class":44},"  useEffect",[34,800,363],{"class":48},[34,802,92],{"class":40},[34,804,805],{"class":48}," { ",[34,807,808],{"class":44},"fetchUser",[34,810,811],{"class":48},"().",[34,813,814],{"class":44},"then",[34,816,817],{"class":48},"(setUser) }, [fetchUser]) ",[34,819,820],{"class":75},"\u002F\u002F new fn each render\n",[34,822,823],{"class":36,"line":132},[34,824,135],{"class":48},[34,826,827],{"class":36,"line":597},[34,828,594],{"emptyLinePlaceholder":593},[34,830,831],{"class":36,"line":603},[34,832,833],{"class":75},"\u002F\u002F with useCallback: refetch fires only when userId changes\n",[34,835,836,838,840,842,844,846,848,850,852],{"class":36,"line":618},[34,837,177],{"class":40},[34,839,778],{"class":58},[34,841,62],{"class":40},[34,843,185],{"class":44},[34,845,363],{"class":48},[34,847,92],{"class":40},[34,849,787],{"class":48},[34,851,790],{"class":44},[34,853,854],{"class":48},"(userId), [userId])\n",[34,856,857,859,861,863,865,867,869,871],{"class":36,"line":639},[34,858,746],{"class":44},[34,860,363],{"class":48},[34,862,92],{"class":40},[34,864,805],{"class":48},[34,866,808],{"class":44},[34,868,811],{"class":48},[34,870,814],{"class":44},[34,872,873],{"class":48},"(setUser) }, [fetchUser])\n",[15,875,876,877,880],{},"An alternative that's often cleaner: move the function ",[19,878,879],{},"inside"," the effect so it's\nnot in the dependency array at all.",[10,882,884],{"id":883},"stabilizing-object-props-with-usememo","Stabilizing object props with useMemo",[15,886,887,888,890],{},"The same referential-equality issue applies to objects and arrays. Wrap them in ",[31,889,149],{},"\nto preserve the reference when the content hasn't changed.",[24,892,894],{"className":26,"code":893,"language":28,"meta":29,"style":29},"\u002F\u002F new object every render -> memoized child always re-renders\n\u003CChart config={{ color: theme, size }} \u002F>\n\n\u002F\u002F stable reference -> child re-renders only when theme or size change\nconst config = useMemo(() => ({ color: theme, size }), [theme, size])\n\u003CChart config={config} \u002F>\n",[31,895,896,901,917,921,926,943],{"__ignoreMap":29},[34,897,898],{"class":36,"line":37},[34,899,900],{"class":75},"\u002F\u002F new object every render -> memoized child always re-renders\n",[34,902,903,906,909,912,914],{"class":36,"line":52},[34,904,905],{"class":48},"\u003C",[34,907,908],{"class":58},"Chart",[34,910,911],{"class":44}," config",[34,913,118],{"class":40},[34,915,916],{"class":48},"{{ color: theme, size }} \u002F>\n",[34,918,919],{"class":36,"line":79},[34,920,594],{"emptyLinePlaceholder":593},[34,922,923],{"class":36,"line":104},[34,924,925],{"class":75},"\u002F\u002F stable reference -> child re-renders only when theme or size change\n",[34,927,928,930,932,934,936,938,940],{"class":36,"line":132},[34,929,177],{"class":40},[34,931,911],{"class":58},[34,933,62],{"class":40},[34,935,257],{"class":44},[34,937,363],{"class":48},[34,939,92],{"class":40},[34,941,942],{"class":48}," ({ color: theme, size }), [theme, size])\n",[34,944,945,947,949,951,953],{"class":36,"line":597},[34,946,905],{"class":48},[34,948,908],{"class":58},[34,950,911],{"class":44},[34,952,118],{"class":40},[34,954,955],{"class":48},"{config} \u002F>\n",[15,957,958],{},"For context providers, memoizing the value object is the fix for \"all consumers\nre-render on every Provider render.\"",[10,960,962],{"id":961},"when-not-to-memoize","When NOT to memoize",[15,964,965,966,146,968,970],{},"This is what most developers miss: ",[31,967,145],{},[31,969,149],{}," have real costs.",[15,972,973],{},"Every render, React must:",[975,976,977,981,987],"ol",{},[978,979,980],"li",{},"Read the stored deps array.",[978,982,983,984,986],{},"Compare each dep with ",[31,985,166],{},".",[978,988,989],{},"Return the cached value or re-run the computation.",[15,991,992,993,996],{},"For trivial computations, this overhead ",[19,994,995],{},"exceeds"," the cost of just re-running the\ncomputation. The React team has publicly said that the overhead of memoization can\nmake performance worse for simple cases.",[24,998,1000],{"className":26,"code":999,"language":28,"meta":29,"style":29},"\u002F\u002F overhead > savings\nconst label = useMemo(() => `Hello ${name}`, [name])\n\n\u002F\u002F just compute it\nconst label = `Hello ${name}`\n",[31,1001,1002,1007,1034,1038,1043],{"__ignoreMap":29},[34,1003,1004],{"class":36,"line":37},[34,1005,1006],{"class":75},"\u002F\u002F overhead > savings\n",[34,1008,1009,1011,1014,1016,1018,1020,1022,1025,1028,1031],{"class":36,"line":52},[34,1010,177],{"class":40},[34,1012,1013],{"class":58}," label",[34,1015,62],{"class":40},[34,1017,257],{"class":44},[34,1019,363],{"class":48},[34,1021,92],{"class":40},[34,1023,1024],{"class":68}," `Hello ${",[34,1026,1027],{"class":48},"name",[34,1029,1030],{"class":68},"}`",[34,1032,1033],{"class":48},", [name])\n",[34,1035,1036],{"class":36,"line":79},[34,1037,594],{"emptyLinePlaceholder":593},[34,1039,1040],{"class":36,"line":104},[34,1041,1042],{"class":75},"\u002F\u002F just compute it\n",[34,1044,1045,1047,1049,1051,1053,1055],{"class":36,"line":132},[34,1046,177],{"class":40},[34,1048,1013],{"class":58},[34,1050,62],{"class":40},[34,1052,1024],{"class":68},[34,1054,1027],{"class":48},[34,1056,1057],{"class":68},"}`\n",[15,1059,1060,1061,1064],{},"Memoize when you have ",[19,1062,1063],{},"evidence"," of a problem (profiling with React DevTools Profiler),\nnot as a default practice. The three cases worth memoizing:",[975,1066,1067,1073,1082],{},[978,1068,1069,1072],{},[19,1070,1071],{},"Genuinely expensive computation"," — filtering\u002Fsorting thousands of items.",[978,1074,1075,1078,1079,1081],{},[19,1076,1077],{},"Stable reference for a memoized child"," — ",[31,1080,141],{}," component receiving object\nor function props.",[978,1083,1084,1087,1088,1090],{},[19,1085,1086],{},"Effect dependency stabilization"," — function or object in a ",[31,1089,746],{}," dep array.",[10,1092,1094],{"id":1093},"stale-closure-the-missing-dependency-bug","Stale closure: the missing dependency bug",[15,1096,1097,1098,1101],{},"Omitting a value from the deps array means the memoized function\u002Fvalue ",[19,1099,1100],{},"closes over a\nstale snapshot"," of that value from the render where it was last computed.",[24,1103,1105],{"className":26,"code":1104,"language":28,"meta":29,"style":29},"\u002F\u002F user is omitted -> greeting always uses the initial user.name\nconst greeting = useMemo(() => `Hi ${user.name}`, []) \u002F\u002F bug: stale forever\n\n\u002F\u002F correct\nconst greeting = useMemo(() => `Hi ${user.name}`, [user.name])\n",[31,1106,1107,1112,1145,1149,1154],{"__ignoreMap":29},[34,1108,1109],{"class":36,"line":37},[34,1110,1111],{"class":75},"\u002F\u002F user is omitted -> greeting always uses the initial user.name\n",[34,1113,1114,1116,1119,1121,1123,1125,1127,1130,1133,1135,1137,1139,1142],{"class":36,"line":52},[34,1115,177],{"class":40},[34,1117,1118],{"class":58}," greeting",[34,1120,62],{"class":40},[34,1122,257],{"class":44},[34,1124,363],{"class":48},[34,1126,92],{"class":40},[34,1128,1129],{"class":68}," `Hi ${",[34,1131,1132],{"class":48},"user",[34,1134,986],{"class":68},[34,1136,1027],{"class":48},[34,1138,1030],{"class":68},[34,1140,1141],{"class":48},", []) ",[34,1143,1144],{"class":75},"\u002F\u002F bug: stale forever\n",[34,1146,1147],{"class":36,"line":79},[34,1148,594],{"emptyLinePlaceholder":593},[34,1150,1151],{"class":36,"line":104},[34,1152,1153],{"class":75},"\u002F\u002F correct\n",[34,1155,1156,1158,1160,1162,1164,1166,1168,1170,1172,1174,1176,1178],{"class":36,"line":132},[34,1157,177],{"class":40},[34,1159,1118],{"class":58},[34,1161,62],{"class":40},[34,1163,257],{"class":44},[34,1165,363],{"class":48},[34,1167,92],{"class":40},[34,1169,1129],{"class":68},[34,1171,1132],{"class":48},[34,1173,986],{"class":68},[34,1175,1027],{"class":48},[34,1177,1030],{"class":68},[34,1179,1180],{"class":48},", [user.name])\n",[15,1182,1183,1184,1187,1188,986],{},"The ",[31,1185,1186],{},"react-hooks\u002Fexhaustive-deps"," ESLint rule catches this. Never suppress the rule to\n\"cache something forever\" — if you genuinely want a value from a specific moment in\ntime, store it in a ",[31,1189,1190],{},"useRef",[10,1192,1194],{"id":1193},"measuring-before-optimizing","Measuring before optimizing",[15,1196,1197],{},"The correct workflow:",[975,1199,1200,1203,1206,1209,1224],{},[978,1201,1202],{},"Notice a perceived performance issue.",[978,1204,1205],{},"Open React DevTools Profiler and record the interaction.",[978,1207,1208],{},"Identify which component is the hot path and why.",[978,1210,1211,1212,1214,1215,1214,1217,1219,1220,986],{},"Apply ",[31,1213,149],{},"\u002F",[31,1216,145],{},[31,1218,141],{}," ",[1221,1222,1223],"em",{},"surgically",[978,1225,1226],{},"Record again to confirm the improvement.",[15,1228,1229,1230,1232],{},"Wrapping everything in ",[31,1231,145],{}," \"just in case\" adds code, slows down renders for\nmost code paths, and makes dependency arrays harder to maintain.",[10,1234,1236],{"id":1235},"common-interview-questions-at-a-glance","Common interview questions at a glance",[1238,1239,1240,1246,1252,1264,1270,1276],"ul",{},[978,1241,1242,1245],{},[19,1243,1244],{},"What does useCallback return?"," A memoized function reference, identical across\nrenders until a dependency changes.",[978,1247,1248,1251],{},[19,1249,1250],{},"What does useMemo return?"," A memoized computed value, recomputed only when deps\nchange.",[978,1253,1254,1257,1258,1260,1261,1263],{},[19,1255,1256],{},"How do they differ?"," Same mechanism; ",[31,1259,145],{}," caches a function,\n",[31,1262,149],{}," caches any value (often the result of calling a function).",[978,1265,1266,1269],{},[19,1267,1268],{},"Why doesn't React.memo work if I don't useCallback?"," Function props are new\nreferences each render; memo sees them as changed.",[978,1271,1272,1275],{},[19,1273,1274],{},"When should you memoize?"," When profiling shows a real problem — not by default.",[978,1277,1278,1281],{},[19,1279,1280],{},"What's the stale closure risk?"," Omitting a dep means the memoized value reads\na frozen old snapshot of that dep forever.",[1283,1284,1285],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":29,"searchDepth":52,"depth":52,"links":1287},[1288,1289,1290,1291,1292,1293,1294,1295,1296,1297,1298,1299],{"id":12,"depth":52,"text":13},{"id":153,"depth":52,"text":145},{"id":230,"depth":52,"text":149},{"id":334,"depth":52,"text":335},{"id":410,"depth":52,"text":411},{"id":486,"depth":52,"text":487},{"id":739,"depth":52,"text":740},{"id":883,"depth":52,"text":884},{"id":961,"depth":52,"text":962},{"id":1093,"depth":52,"text":1094},{"id":1193,"depth":52,"text":1194},{"id":1235,"depth":52,"text":1236},"React useCallback and useMemo interview guide — referential stability, when to memoize, dependency arrays, React.memo interaction, and avoiding premature optimization.","hard","md","React","react",{},"\u002Fblog\u002Freact-usecallback-usememo-guide","\u002Freact\u002Fhooks\u002Fusecallback-usememo",{"title":5,"description":1300},"blog\u002Freact-usecallback-usememo-guide","useCallback & useMemo","Hooks","hooks","2026-06-23","y4r_PifRHVVfxXbEDmYbKrHX5nftmscH403ShVGOjv8",1782244083417]