[{"data":1,"prerenderedAt":2485},["ShallowReactive",2],{"page-\u002Freact\u002F08-useref-and-dom-access":3},{"id":4,"title":5,"body":6,"description":2478,"extension":2479,"meta":2480,"navigation":62,"path":2481,"seo":2482,"stem":2483,"__hash__":2484},"content\u002Freact\u002F08-useref-and-dom-access.md","08 — useRef & DOM Access",{"type":7,"value":8,"toc":2466},"minimark",[9,19,24,273,277,590,594,882,886,1242,1246,1542,1546,1939,1943,2032,2036,2130,2134,2138,2226,2370,2374,2463],[10,11,13,14,18],"h1",{"id":12},"_08-useref-dom-access","08 — ",[15,16,17],"code",{},"useRef"," & DOM Access",[20,21,23],"h2",{"id":22},"useref-mutable-container-that-survives-renders","useRef: Mutable Container That Survives Renders",[25,26,29],"code-wrapper",{"filename":27,"language":28},"useref_basics.js","javascript",[30,31,35],"pre",{"className":32,"code":33,"language":28,"meta":34,"style":34},"language-javascript shiki shiki-themes github-light github-dark","import { useRef } from 'react'\n\n\u002F\u002F useRef returns a STABLE object { current: initialValue } that persists across\n\u002F\u002F renders. Mutating .current does NOT trigger a re-render.\n\nconst timerRef = useRef(null)\n\u002F\u002F timerRef === timerRef across all renders (same object reference)\n\u002F\u002F timerRef.current can be mutated freely without causing re-renders\n\n\u002F\u002F useRef vs useState:\n\u002F\u002F   useState:   changing value → triggers re-render → UI updates\n\u002F\u002F   useRef:     changing value → NO re-render → just a mutable container\n\u002F\u002F\n\u002F\u002F Use useState when the value drives the UI.\n\u002F\u002F Use useRef when the value is an internal implementation detail (timers,\n\u002F\u002F interval IDs, \"previous value\" trackers, mutable flags) that the UI\n\u002F\u002F doesn't need to react to.\n\n\u002F\u002F ANTI-PATTERN: using useRef for state that should trigger re-render\nfunction BadCounter() {\n  const countRef = useRef(0)\n  return \u003Cbutton onClick={() => countRef.current++}>{countRef.current}\u003C\u002Fbutton>\n  \u002F\u002F The button text NEVER updates — mutating ref.current doesn't re-render.\n  \u002F\u002F The count IS incrementing internally, but the UI is frozen at 0.\n}\n","",[15,36,37,57,64,71,77,82,108,114,120,125,131,137,143,149,155,161,167,173,178,184,196,216,255,261,267],{"__ignoreMap":34},[38,39,42,46,50,53],"span",{"class":40,"line":41},"line",1,[38,43,45],{"class":44},"svdQ7","import",[38,47,49],{"class":48},"ssxIu"," { useRef } ",[38,51,52],{"class":44},"from",[38,54,56],{"class":55},"sJ6F3"," 'react'\n",[38,58,60],{"class":40,"line":59},2,[38,61,63],{"emptyLinePlaceholder":62},true,"\n",[38,65,67],{"class":40,"line":66},3,[38,68,70],{"class":69},"sdCPZ","\u002F\u002F useRef returns a STABLE object { current: initialValue } that persists across\n",[38,72,74],{"class":40,"line":73},4,[38,75,76],{"class":69},"\u002F\u002F renders. Mutating .current does NOT trigger a re-render.\n",[38,78,80],{"class":40,"line":79},5,[38,81,63],{"emptyLinePlaceholder":62},[38,83,85,88,92,95,99,102,105],{"class":40,"line":84},6,[38,86,87],{"class":44},"const",[38,89,91],{"class":90},"snvgF"," timerRef",[38,93,94],{"class":44}," =",[38,96,98],{"class":97},"sIsaT"," useRef",[38,100,101],{"class":48},"(",[38,103,104],{"class":90},"null",[38,106,107],{"class":48},")\n",[38,109,111],{"class":40,"line":110},7,[38,112,113],{"class":69},"\u002F\u002F timerRef === timerRef across all renders (same object reference)\n",[38,115,117],{"class":40,"line":116},8,[38,118,119],{"class":69},"\u002F\u002F timerRef.current can be mutated freely without causing re-renders\n",[38,121,123],{"class":40,"line":122},9,[38,124,63],{"emptyLinePlaceholder":62},[38,126,128],{"class":40,"line":127},10,[38,129,130],{"class":69},"\u002F\u002F useRef vs useState:\n",[38,132,134],{"class":40,"line":133},11,[38,135,136],{"class":69},"\u002F\u002F   useState:   changing value → triggers re-render → UI updates\n",[38,138,140],{"class":40,"line":139},12,[38,141,142],{"class":69},"\u002F\u002F   useRef:     changing value → NO re-render → just a mutable container\n",[38,144,146],{"class":40,"line":145},13,[38,147,148],{"class":69},"\u002F\u002F\n",[38,150,152],{"class":40,"line":151},14,[38,153,154],{"class":69},"\u002F\u002F Use useState when the value drives the UI.\n",[38,156,158],{"class":40,"line":157},15,[38,159,160],{"class":69},"\u002F\u002F Use useRef when the value is an internal implementation detail (timers,\n",[38,162,164],{"class":40,"line":163},16,[38,165,166],{"class":69},"\u002F\u002F interval IDs, \"previous value\" trackers, mutable flags) that the UI\n",[38,168,170],{"class":40,"line":169},17,[38,171,172],{"class":69},"\u002F\u002F doesn't need to react to.\n",[38,174,176],{"class":40,"line":175},18,[38,177,63],{"emptyLinePlaceholder":62},[38,179,181],{"class":40,"line":180},19,[38,182,183],{"class":69},"\u002F\u002F ANTI-PATTERN: using useRef for state that should trigger re-render\n",[38,185,187,190,193],{"class":40,"line":186},20,[38,188,189],{"class":44},"function",[38,191,192],{"class":97}," BadCounter",[38,194,195],{"class":48},"() {\n",[38,197,199,202,205,207,209,211,214],{"class":40,"line":198},21,[38,200,201],{"class":44},"  const",[38,203,204],{"class":90}," countRef",[38,206,94],{"class":44},[38,208,98],{"class":97},[38,210,101],{"class":48},[38,212,213],{"class":90},"0",[38,215,107],{"class":48},[38,217,219,222,225,229,232,235,238,241,244,247,250,252],{"class":40,"line":218},22,[38,220,221],{"class":44},"  return",[38,223,224],{"class":48}," \u003C",[38,226,228],{"class":227},"sk71V","button",[38,230,231],{"class":97}," onClick",[38,233,234],{"class":44},"=",[38,236,237],{"class":48},"{() ",[38,239,240],{"class":44},"=>",[38,242,243],{"class":48}," countRef.current",[38,245,246],{"class":44},"++",[38,248,249],{"class":48},"}>{countRef.current}\u003C\u002F",[38,251,228],{"class":227},[38,253,254],{"class":48},">\n",[38,256,258],{"class":40,"line":257},23,[38,259,260],{"class":69},"  \u002F\u002F The button text NEVER updates — mutating ref.current doesn't re-render.\n",[38,262,264],{"class":40,"line":263},24,[38,265,266],{"class":69},"  \u002F\u002F The count IS incrementing internally, but the UI is frozen at 0.\n",[38,268,270],{"class":40,"line":269},25,[38,271,272],{"class":48},"}\n",[20,274,276],{"id":275},"dom-access-with-refs","DOM Access with Refs",[25,278,280],{"filename":279,"language":28},"dom_access.js",[30,281,283],{"className":32,"code":282,"language":28,"meta":34,"style":34},"function AutoFocusInput() {\n  const inputRef = useRef(null)\n\n  useEffect(() => {\n    \u002F\u002F After mount, the ref's .current points to the actual \u003Cinput> DOM node\n    inputRef.current?.focus()\n  }, [])  \u002F\u002F run once on mount\n\n  return \u003Cinput ref={inputRef} type=\"text\" \u002F>\n  \u002F\u002F React assigns the DOM node to inputRef.current after the initial render.\n  \u002F\u002F On unmount, React sets inputRef.current back to null.\n}\n\n\u002F\u002F CALLBACK REF: when you need to know WHEN the node changes\nfunction MeasuredBox({ children }) {\n  const [height, setHeight] = useState(0)\n\n  \u002F\u002F Callback ref fires when the DOM node is created, destroyed, or changes\n  const refCallback = useCallback((node) => {\n    if (node) {\n      \u002F\u002F node is the DOM element — measure it\n      setHeight(node.getBoundingClientRect().height)\n    }\n    \u002F\u002F node === null when unmounting — cleanup if needed\n  }, [])\n\n  return \u003Cdiv ref={refCallback} style={{ height: height || 'auto' }}>{children}\u003C\u002Fdiv>\n}\n\u002F\u002F Callback refs are useful when you need to run logic at the exact moment\n\u002F\u002F the DOM node is attached\u002Fdetached, not just on mount.\n",[15,284,285,294,311,315,328,333,344,352,356,384,389,394,398,402,407,424,454,458,463,488,496,501,515,520,525,530,535,573,578,584],{"__ignoreMap":34},[38,286,287,289,292],{"class":40,"line":41},[38,288,189],{"class":44},[38,290,291],{"class":97}," AutoFocusInput",[38,293,195],{"class":48},[38,295,296,298,301,303,305,307,309],{"class":40,"line":59},[38,297,201],{"class":44},[38,299,300],{"class":90}," inputRef",[38,302,94],{"class":44},[38,304,98],{"class":97},[38,306,101],{"class":48},[38,308,104],{"class":90},[38,310,107],{"class":48},[38,312,313],{"class":40,"line":66},[38,314,63],{"emptyLinePlaceholder":62},[38,316,317,320,323,325],{"class":40,"line":73},[38,318,319],{"class":97},"  useEffect",[38,321,322],{"class":48},"(() ",[38,324,240],{"class":44},[38,326,327],{"class":48}," {\n",[38,329,330],{"class":40,"line":79},[38,331,332],{"class":69},"    \u002F\u002F After mount, the ref's .current points to the actual \u003Cinput> DOM node\n",[38,334,335,338,341],{"class":40,"line":84},[38,336,337],{"class":48},"    inputRef.current?.",[38,339,340],{"class":97},"focus",[38,342,343],{"class":48},"()\n",[38,345,346,349],{"class":40,"line":110},[38,347,348],{"class":48},"  }, [])  ",[38,350,351],{"class":69},"\u002F\u002F run once on mount\n",[38,353,354],{"class":40,"line":116},[38,355,63],{"emptyLinePlaceholder":62},[38,357,358,360,362,365,368,370,373,376,378,381],{"class":40,"line":122},[38,359,221],{"class":44},[38,361,224],{"class":48},[38,363,364],{"class":227},"input",[38,366,367],{"class":97}," ref",[38,369,234],{"class":44},[38,371,372],{"class":48},"{inputRef} ",[38,374,375],{"class":97},"type",[38,377,234],{"class":44},[38,379,380],{"class":55},"\"text\"",[38,382,383],{"class":48}," \u002F>\n",[38,385,386],{"class":40,"line":127},[38,387,388],{"class":69},"  \u002F\u002F React assigns the DOM node to inputRef.current after the initial render.\n",[38,390,391],{"class":40,"line":133},[38,392,393],{"class":69},"  \u002F\u002F On unmount, React sets inputRef.current back to null.\n",[38,395,396],{"class":40,"line":139},[38,397,272],{"class":48},[38,399,400],{"class":40,"line":145},[38,401,63],{"emptyLinePlaceholder":62},[38,403,404],{"class":40,"line":151},[38,405,406],{"class":69},"\u002F\u002F CALLBACK REF: when you need to know WHEN the node changes\n",[38,408,409,411,414,417,421],{"class":40,"line":157},[38,410,189],{"class":44},[38,412,413],{"class":97}," MeasuredBox",[38,415,416],{"class":48},"({ ",[38,418,420],{"class":419},"sCrzJ","children",[38,422,423],{"class":48}," }) {\n",[38,425,426,428,431,434,437,440,443,445,448,450,452],{"class":40,"line":163},[38,427,201],{"class":44},[38,429,430],{"class":48}," [",[38,432,433],{"class":90},"height",[38,435,436],{"class":48},", ",[38,438,439],{"class":90},"setHeight",[38,441,442],{"class":48},"] ",[38,444,234],{"class":44},[38,446,447],{"class":97}," useState",[38,449,101],{"class":48},[38,451,213],{"class":90},[38,453,107],{"class":48},[38,455,456],{"class":40,"line":169},[38,457,63],{"emptyLinePlaceholder":62},[38,459,460],{"class":40,"line":175},[38,461,462],{"class":69},"  \u002F\u002F Callback ref fires when the DOM node is created, destroyed, or changes\n",[38,464,465,467,470,472,475,478,481,484,486],{"class":40,"line":180},[38,466,201],{"class":44},[38,468,469],{"class":90}," refCallback",[38,471,94],{"class":44},[38,473,474],{"class":97}," useCallback",[38,476,477],{"class":48},"((",[38,479,480],{"class":419},"node",[38,482,483],{"class":48},") ",[38,485,240],{"class":44},[38,487,327],{"class":48},[38,489,490,493],{"class":40,"line":186},[38,491,492],{"class":44},"    if",[38,494,495],{"class":48}," (node) {\n",[38,497,498],{"class":40,"line":198},[38,499,500],{"class":69},"      \u002F\u002F node is the DOM element — measure it\n",[38,502,503,506,509,512],{"class":40,"line":218},[38,504,505],{"class":97},"      setHeight",[38,507,508],{"class":48},"(node.",[38,510,511],{"class":97},"getBoundingClientRect",[38,513,514],{"class":48},"().height)\n",[38,516,517],{"class":40,"line":257},[38,518,519],{"class":48},"    }\n",[38,521,522],{"class":40,"line":263},[38,523,524],{"class":69},"    \u002F\u002F node === null when unmounting — cleanup if needed\n",[38,526,527],{"class":40,"line":269},[38,528,529],{"class":48},"  }, [])\n",[38,531,533],{"class":40,"line":532},26,[38,534,63],{"emptyLinePlaceholder":62},[38,536,538,540,542,545,547,549,552,555,557,560,563,566,569,571],{"class":40,"line":537},27,[38,539,221],{"class":44},[38,541,224],{"class":48},[38,543,544],{"class":227},"div",[38,546,367],{"class":97},[38,548,234],{"class":44},[38,550,551],{"class":48},"{refCallback} ",[38,553,554],{"class":97},"style",[38,556,234],{"class":44},[38,558,559],{"class":48},"{{ height: height ",[38,561,562],{"class":44},"||",[38,564,565],{"class":55}," 'auto'",[38,567,568],{"class":48}," }}>{children}\u003C\u002F",[38,570,544],{"class":227},[38,572,254],{"class":48},[38,574,576],{"class":40,"line":575},28,[38,577,272],{"class":48},[38,579,581],{"class":40,"line":580},29,[38,582,583],{"class":69},"\u002F\u002F Callback refs are useful when you need to run logic at the exact moment\n",[38,585,587],{"class":40,"line":586},30,[38,588,589],{"class":69},"\u002F\u002F the DOM node is attached\u002Fdetached, not just on mount.\n",[20,591,593],{"id":592},"focus-management","Focus Management",[25,595,597],{"filename":596,"language":28},"focus_management.js",[30,598,600],{"className":32,"code":599,"language":28,"meta":34,"style":34},"function Modal({ isOpen, onClose, children }) {\n  const modalRef = useRef(null)\n  const previouslyFocused = useRef(null)\n\n  useEffect(() => {\n    if (isOpen) {\n      \u002F\u002F Save the element that had focus before the modal opened\n      previouslyFocused.current = document.activeElement\n      \u002F\u002F Move focus into the modal\n      modalRef.current?.focus()\n    } else {\n      \u002F\u002F Restore focus to the trigger when modal closes (accessibility)\n      previouslyFocused.current?.focus()\n    }\n  }, [isOpen])\n\n  return isOpen ? (\n    \u003Cdiv className=\"overlay\" onClick={onClose}>\n      \u003Cdiv ref={modalRef} tabIndex={-1} onClick={e => e.stopPropagation()}>\n        {\u002F* tabIndex={-1} makes the div focusable programmatically (not in tab order) *\u002F}\n        {children}\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  ) : null\n}\n",[15,601,602,625,642,659,663,673,680,685,695,700,709,719,724,733,737,742,746,759,781,834,844,849,858,867,878],{"__ignoreMap":34},[38,603,604,606,609,611,614,616,619,621,623],{"class":40,"line":41},[38,605,189],{"class":44},[38,607,608],{"class":97}," Modal",[38,610,416],{"class":48},[38,612,613],{"class":419},"isOpen",[38,615,436],{"class":48},[38,617,618],{"class":419},"onClose",[38,620,436],{"class":48},[38,622,420],{"class":419},[38,624,423],{"class":48},[38,626,627,629,632,634,636,638,640],{"class":40,"line":59},[38,628,201],{"class":44},[38,630,631],{"class":90}," modalRef",[38,633,94],{"class":44},[38,635,98],{"class":97},[38,637,101],{"class":48},[38,639,104],{"class":90},[38,641,107],{"class":48},[38,643,644,646,649,651,653,655,657],{"class":40,"line":66},[38,645,201],{"class":44},[38,647,648],{"class":90}," previouslyFocused",[38,650,94],{"class":44},[38,652,98],{"class":97},[38,654,101],{"class":48},[38,656,104],{"class":90},[38,658,107],{"class":48},[38,660,661],{"class":40,"line":73},[38,662,63],{"emptyLinePlaceholder":62},[38,664,665,667,669,671],{"class":40,"line":79},[38,666,319],{"class":97},[38,668,322],{"class":48},[38,670,240],{"class":44},[38,672,327],{"class":48},[38,674,675,677],{"class":40,"line":84},[38,676,492],{"class":44},[38,678,679],{"class":48}," (isOpen) {\n",[38,681,682],{"class":40,"line":110},[38,683,684],{"class":69},"      \u002F\u002F Save the element that had focus before the modal opened\n",[38,686,687,690,692],{"class":40,"line":116},[38,688,689],{"class":48},"      previouslyFocused.current ",[38,691,234],{"class":44},[38,693,694],{"class":48}," document.activeElement\n",[38,696,697],{"class":40,"line":122},[38,698,699],{"class":69},"      \u002F\u002F Move focus into the modal\n",[38,701,702,705,707],{"class":40,"line":127},[38,703,704],{"class":48},"      modalRef.current?.",[38,706,340],{"class":97},[38,708,343],{"class":48},[38,710,711,714,717],{"class":40,"line":133},[38,712,713],{"class":48},"    } ",[38,715,716],{"class":44},"else",[38,718,327],{"class":48},[38,720,721],{"class":40,"line":139},[38,722,723],{"class":69},"      \u002F\u002F Restore focus to the trigger when modal closes (accessibility)\n",[38,725,726,729,731],{"class":40,"line":145},[38,727,728],{"class":48},"      previouslyFocused.current?.",[38,730,340],{"class":97},[38,732,343],{"class":48},[38,734,735],{"class":40,"line":151},[38,736,519],{"class":48},[38,738,739],{"class":40,"line":157},[38,740,741],{"class":48},"  }, [isOpen])\n",[38,743,744],{"class":40,"line":163},[38,745,63],{"emptyLinePlaceholder":62},[38,747,748,750,753,756],{"class":40,"line":169},[38,749,221],{"class":44},[38,751,752],{"class":48}," isOpen ",[38,754,755],{"class":44},"?",[38,757,758],{"class":48}," (\n",[38,760,761,764,766,769,771,774,776,778],{"class":40,"line":175},[38,762,763],{"class":48},"    \u003C",[38,765,544],{"class":227},[38,767,768],{"class":97}," className",[38,770,234],{"class":44},[38,772,773],{"class":55},"\"overlay\"",[38,775,231],{"class":97},[38,777,234],{"class":44},[38,779,780],{"class":48},"{onClose}>\n",[38,782,783,786,788,790,792,795,798,800,803,806,809,812,815,817,819,822,825,828,831],{"class":40,"line":180},[38,784,785],{"class":48},"      \u003C",[38,787,544],{"class":227},[38,789,367],{"class":97},[38,791,234],{"class":44},[38,793,794],{"class":48},"{modalRef} ",[38,796,797],{"class":97},"tabIndex",[38,799,234],{"class":44},[38,801,802],{"class":48},"{",[38,804,805],{"class":44},"-",[38,807,808],{"class":90},"1",[38,810,811],{"class":48},"} ",[38,813,814],{"class":97},"onClick",[38,816,234],{"class":44},[38,818,802],{"class":48},[38,820,821],{"class":419},"e",[38,823,824],{"class":44}," =>",[38,826,827],{"class":48}," e.",[38,829,830],{"class":97},"stopPropagation",[38,832,833],{"class":48},"()}>\n",[38,835,836,839,842],{"class":40,"line":186},[38,837,838],{"class":48},"        {",[38,840,841],{"class":69},"\u002F* tabIndex={-1} makes the div focusable programmatically (not in tab order) *\u002F",[38,843,272],{"class":48},[38,845,846],{"class":40,"line":198},[38,847,848],{"class":48},"        {children}\n",[38,850,851,854,856],{"class":40,"line":218},[38,852,853],{"class":48},"      \u003C\u002F",[38,855,544],{"class":227},[38,857,254],{"class":48},[38,859,860,863,865],{"class":40,"line":257},[38,861,862],{"class":48},"    \u003C\u002F",[38,864,544],{"class":227},[38,866,254],{"class":48},[38,868,869,872,875],{"class":40,"line":263},[38,870,871],{"class":48},"  ) ",[38,873,874],{"class":44},":",[38,876,877],{"class":90}," null\n",[38,879,880],{"class":40,"line":269},[38,881,272],{"class":48},[20,883,885],{"id":884},"layout-measurement-with-uselayouteffect","Layout Measurement with useLayoutEffect",[25,887,889],{"filename":888,"language":28},"layout_measurement.js",[30,890,892],{"className":32,"code":891,"language":28,"meta":34,"style":34},"import { useLayoutEffect, useRef, useState } from 'react'\n\n\u002F\u002F useLayoutEffect runs SYNCHRONOUSLY after DOM mutations but BEFORE the browser\n\u002F\u002F paints. Use it to read layout (dimensions, position) and make adjustments\n\u002F\u002F that should be visible without flicker.\n\u002F\u002F\n\u002F\u002F useEffect runs AFTER paint — if you measure and then setState, the user sees\n\u002F\u002F a flash of the wrong layout before the correction. useLayoutEffect prevents this.\n\nfunction Tooltip({ targetRef, content }) {\n  const tooltipRef = useRef(null)\n  const [position, setPosition] = useState({ top: 0, left: 0 })\n\n  useLayoutEffect(() => {\n    if (!targetRef.current || !tooltipRef.current) return\n\n    const targetRect = targetRef.current.getBoundingClientRect()\n    const tooltipRect = tooltipRef.current.getBoundingClientRect()\n\n    \u002F\u002F Position tooltip above the target, centered\n    setPosition({\n      top: targetRect.top - tooltipRect.height - 8,\n      left: targetRect.left + (targetRect.width - tooltipRect.width) \u002F 2,\n    })\n  }, [targetRef])  \u002F\u002F re-measure when target changes\n\n  return (\n    \u003Cdiv ref={tooltipRef} style={{ position: 'fixed', ...position }}>\n      {content}\n    \u003C\u002Fdiv>\n  )\n}\n\u002F\u002F CAVEAT: useLayoutEffect blocks painting — only use for measurements that\n\u002F\u002F would cause visible flicker if done in useEffect. For everything else,\n\u002F\u002F use useEffect (non-blocking).\n",[15,893,894,905,909,914,919,924,928,933,938,942,961,978,1011,1015,1026,1050,1054,1071,1087,1091,1096,1104,1122,1146,1151,1159,1163,1169,1200,1205,1213,1219,1224,1230,1236],{"__ignoreMap":34},[38,895,896,898,901,903],{"class":40,"line":41},[38,897,45],{"class":44},[38,899,900],{"class":48}," { useLayoutEffect, useRef, useState } ",[38,902,52],{"class":44},[38,904,56],{"class":55},[38,906,907],{"class":40,"line":59},[38,908,63],{"emptyLinePlaceholder":62},[38,910,911],{"class":40,"line":66},[38,912,913],{"class":69},"\u002F\u002F useLayoutEffect runs SYNCHRONOUSLY after DOM mutations but BEFORE the browser\n",[38,915,916],{"class":40,"line":73},[38,917,918],{"class":69},"\u002F\u002F paints. Use it to read layout (dimensions, position) and make adjustments\n",[38,920,921],{"class":40,"line":79},[38,922,923],{"class":69},"\u002F\u002F that should be visible without flicker.\n",[38,925,926],{"class":40,"line":84},[38,927,148],{"class":69},[38,929,930],{"class":40,"line":110},[38,931,932],{"class":69},"\u002F\u002F useEffect runs AFTER paint — if you measure and then setState, the user sees\n",[38,934,935],{"class":40,"line":116},[38,936,937],{"class":69},"\u002F\u002F a flash of the wrong layout before the correction. useLayoutEffect prevents this.\n",[38,939,940],{"class":40,"line":122},[38,941,63],{"emptyLinePlaceholder":62},[38,943,944,946,949,951,954,956,959],{"class":40,"line":127},[38,945,189],{"class":44},[38,947,948],{"class":97}," Tooltip",[38,950,416],{"class":48},[38,952,953],{"class":419},"targetRef",[38,955,436],{"class":48},[38,957,958],{"class":419},"content",[38,960,423],{"class":48},[38,962,963,965,968,970,972,974,976],{"class":40,"line":133},[38,964,201],{"class":44},[38,966,967],{"class":90}," tooltipRef",[38,969,94],{"class":44},[38,971,98],{"class":97},[38,973,101],{"class":48},[38,975,104],{"class":90},[38,977,107],{"class":48},[38,979,980,982,984,987,989,992,994,996,998,1001,1003,1006,1008],{"class":40,"line":139},[38,981,201],{"class":44},[38,983,430],{"class":48},[38,985,986],{"class":90},"position",[38,988,436],{"class":48},[38,990,991],{"class":90},"setPosition",[38,993,442],{"class":48},[38,995,234],{"class":44},[38,997,447],{"class":97},[38,999,1000],{"class":48},"({ top: ",[38,1002,213],{"class":90},[38,1004,1005],{"class":48},", left: ",[38,1007,213],{"class":90},[38,1009,1010],{"class":48}," })\n",[38,1012,1013],{"class":40,"line":145},[38,1014,63],{"emptyLinePlaceholder":62},[38,1016,1017,1020,1022,1024],{"class":40,"line":151},[38,1018,1019],{"class":97},"  useLayoutEffect",[38,1021,322],{"class":48},[38,1023,240],{"class":44},[38,1025,327],{"class":48},[38,1027,1028,1030,1033,1036,1039,1041,1044,1047],{"class":40,"line":157},[38,1029,492],{"class":44},[38,1031,1032],{"class":48}," (",[38,1034,1035],{"class":44},"!",[38,1037,1038],{"class":48},"targetRef.current ",[38,1040,562],{"class":44},[38,1042,1043],{"class":44}," !",[38,1045,1046],{"class":48},"tooltipRef.current) ",[38,1048,1049],{"class":44},"return\n",[38,1051,1052],{"class":40,"line":163},[38,1053,63],{"emptyLinePlaceholder":62},[38,1055,1056,1059,1062,1064,1067,1069],{"class":40,"line":169},[38,1057,1058],{"class":44},"    const",[38,1060,1061],{"class":90}," targetRect",[38,1063,94],{"class":44},[38,1065,1066],{"class":48}," targetRef.current.",[38,1068,511],{"class":97},[38,1070,343],{"class":48},[38,1072,1073,1075,1078,1080,1083,1085],{"class":40,"line":175},[38,1074,1058],{"class":44},[38,1076,1077],{"class":90}," tooltipRect",[38,1079,94],{"class":44},[38,1081,1082],{"class":48}," tooltipRef.current.",[38,1084,511],{"class":97},[38,1086,343],{"class":48},[38,1088,1089],{"class":40,"line":180},[38,1090,63],{"emptyLinePlaceholder":62},[38,1092,1093],{"class":40,"line":186},[38,1094,1095],{"class":69},"    \u002F\u002F Position tooltip above the target, centered\n",[38,1097,1098,1101],{"class":40,"line":198},[38,1099,1100],{"class":97},"    setPosition",[38,1102,1103],{"class":48},"({\n",[38,1105,1106,1109,1111,1114,1116,1119],{"class":40,"line":218},[38,1107,1108],{"class":48},"      top: targetRect.top ",[38,1110,805],{"class":44},[38,1112,1113],{"class":48}," tooltipRect.height ",[38,1115,805],{"class":44},[38,1117,1118],{"class":90}," 8",[38,1120,1121],{"class":48},",\n",[38,1123,1124,1127,1130,1133,1135,1138,1141,1144],{"class":40,"line":257},[38,1125,1126],{"class":48},"      left: targetRect.left ",[38,1128,1129],{"class":44},"+",[38,1131,1132],{"class":48}," (targetRect.width ",[38,1134,805],{"class":44},[38,1136,1137],{"class":48}," tooltipRect.width) ",[38,1139,1140],{"class":44},"\u002F",[38,1142,1143],{"class":90}," 2",[38,1145,1121],{"class":48},[38,1147,1148],{"class":40,"line":263},[38,1149,1150],{"class":48},"    })\n",[38,1152,1153,1156],{"class":40,"line":269},[38,1154,1155],{"class":48},"  }, [targetRef])  ",[38,1157,1158],{"class":69},"\u002F\u002F re-measure when target changes\n",[38,1160,1161],{"class":40,"line":532},[38,1162,63],{"emptyLinePlaceholder":62},[38,1164,1165,1167],{"class":40,"line":537},[38,1166,221],{"class":44},[38,1168,758],{"class":48},[38,1170,1171,1173,1175,1177,1179,1182,1184,1186,1189,1192,1194,1197],{"class":40,"line":575},[38,1172,763],{"class":48},[38,1174,544],{"class":227},[38,1176,367],{"class":97},[38,1178,234],{"class":44},[38,1180,1181],{"class":48},"{tooltipRef} ",[38,1183,554],{"class":97},[38,1185,234],{"class":44},[38,1187,1188],{"class":48},"{{ position: ",[38,1190,1191],{"class":55},"'fixed'",[38,1193,436],{"class":48},[38,1195,1196],{"class":44},"...",[38,1198,1199],{"class":48},"position }}>\n",[38,1201,1202],{"class":40,"line":580},[38,1203,1204],{"class":48},"      {content}\n",[38,1206,1207,1209,1211],{"class":40,"line":586},[38,1208,862],{"class":48},[38,1210,544],{"class":227},[38,1212,254],{"class":48},[38,1214,1216],{"class":40,"line":1215},31,[38,1217,1218],{"class":48},"  )\n",[38,1220,1222],{"class":40,"line":1221},32,[38,1223,272],{"class":48},[38,1225,1227],{"class":40,"line":1226},33,[38,1228,1229],{"class":69},"\u002F\u002F CAVEAT: useLayoutEffect blocks painting — only use for measurements that\n",[38,1231,1233],{"class":40,"line":1232},34,[38,1234,1235],{"class":69},"\u002F\u002F would cause visible flicker if done in useEffect. For everything else,\n",[38,1237,1239],{"class":40,"line":1238},35,[38,1240,1241],{"class":69},"\u002F\u002F use useEffect (non-blocking).\n",[20,1243,1245],{"id":1244},"integrating-with-non-react-libraries","Integrating with Non-React Libraries",[25,1247,1249],{"filename":1248,"language":28},"third_party_integration.js",[30,1250,1252],{"className":32,"code":1251,"language":28,"meta":34,"style":34},"function Chart({ data }) {\n  const canvasRef = useRef(null)\n  const chartRef = useRef(null)  \u002F\u002F holds the Chart.js instance (not DOM)\n\n  useEffect(() => {\n    \u002F\u002F Initialize the third-party library on the DOM node\n    chartRef.current = new Chart(canvasRef.current, {\n      type: 'bar',\n      data: { labels: data.map(d => d.label), datasets: [{ data: data.map(d => d.value) }] },\n    })\n\n    \u002F\u002F Cleanup: destroy the instance to prevent memory leaks\n    return () => chartRef.current?.destroy()\n  }, [])  \u002F\u002F create once on mount\n\n  useEffect(() => {\n    \u002F\u002F Update the chart when data changes (separate from initialization)\n    if (chartRef.current) {\n      chartRef.current.data = {\n        labels: data.map(d => d.label),\n        datasets: [{ data: data.map(d => d.value) }],\n      }\n      chartRef.current.update()\n    }\n  }, [data])\n\n  return \u003Ccanvas ref={canvasRef} \u002F>\n}\n\u002F\u002F Pattern: one effect for initialization (deps=[]), another for updates (deps=[data]).\n\u002F\u002F Always destroy\u002Fdispose the library instance in the init effect's cleanup.\n",[15,1253,1254,1268,1285,1306,1310,1320,1325,1340,1350,1379,1383,1387,1392,1410,1417,1421,1431,1436,1443,1452,1468,1484,1489,1499,1503,1508,1512,1528,1532,1537],{"__ignoreMap":34},[38,1255,1256,1258,1261,1263,1266],{"class":40,"line":41},[38,1257,189],{"class":44},[38,1259,1260],{"class":97}," Chart",[38,1262,416],{"class":48},[38,1264,1265],{"class":419},"data",[38,1267,423],{"class":48},[38,1269,1270,1272,1275,1277,1279,1281,1283],{"class":40,"line":59},[38,1271,201],{"class":44},[38,1273,1274],{"class":90}," canvasRef",[38,1276,94],{"class":44},[38,1278,98],{"class":97},[38,1280,101],{"class":48},[38,1282,104],{"class":90},[38,1284,107],{"class":48},[38,1286,1287,1289,1292,1294,1296,1298,1300,1303],{"class":40,"line":66},[38,1288,201],{"class":44},[38,1290,1291],{"class":90}," chartRef",[38,1293,94],{"class":44},[38,1295,98],{"class":97},[38,1297,101],{"class":48},[38,1299,104],{"class":90},[38,1301,1302],{"class":48},")  ",[38,1304,1305],{"class":69},"\u002F\u002F holds the Chart.js instance (not DOM)\n",[38,1307,1308],{"class":40,"line":73},[38,1309,63],{"emptyLinePlaceholder":62},[38,1311,1312,1314,1316,1318],{"class":40,"line":79},[38,1313,319],{"class":97},[38,1315,322],{"class":48},[38,1317,240],{"class":44},[38,1319,327],{"class":48},[38,1321,1322],{"class":40,"line":84},[38,1323,1324],{"class":69},"    \u002F\u002F Initialize the third-party library on the DOM node\n",[38,1326,1327,1330,1332,1335,1337],{"class":40,"line":110},[38,1328,1329],{"class":48},"    chartRef.current ",[38,1331,234],{"class":44},[38,1333,1334],{"class":44}," new",[38,1336,1260],{"class":97},[38,1338,1339],{"class":48},"(canvasRef.current, {\n",[38,1341,1342,1345,1348],{"class":40,"line":116},[38,1343,1344],{"class":48},"      type: ",[38,1346,1347],{"class":55},"'bar'",[38,1349,1121],{"class":48},[38,1351,1352,1355,1358,1360,1363,1365,1368,1370,1372,1374,1376],{"class":40,"line":122},[38,1353,1354],{"class":48},"      data: { labels: data.",[38,1356,1357],{"class":97},"map",[38,1359,101],{"class":48},[38,1361,1362],{"class":419},"d",[38,1364,824],{"class":44},[38,1366,1367],{"class":48}," d.label), datasets: [{ data: data.",[38,1369,1357],{"class":97},[38,1371,101],{"class":48},[38,1373,1362],{"class":419},[38,1375,824],{"class":44},[38,1377,1378],{"class":48}," d.value) }] },\n",[38,1380,1381],{"class":40,"line":127},[38,1382,1150],{"class":48},[38,1384,1385],{"class":40,"line":133},[38,1386,63],{"emptyLinePlaceholder":62},[38,1388,1389],{"class":40,"line":139},[38,1390,1391],{"class":69},"    \u002F\u002F Cleanup: destroy the instance to prevent memory leaks\n",[38,1393,1394,1397,1400,1402,1405,1408],{"class":40,"line":145},[38,1395,1396],{"class":44},"    return",[38,1398,1399],{"class":48}," () ",[38,1401,240],{"class":44},[38,1403,1404],{"class":48}," chartRef.current?.",[38,1406,1407],{"class":97},"destroy",[38,1409,343],{"class":48},[38,1411,1412,1414],{"class":40,"line":151},[38,1413,348],{"class":48},[38,1415,1416],{"class":69},"\u002F\u002F create once on mount\n",[38,1418,1419],{"class":40,"line":157},[38,1420,63],{"emptyLinePlaceholder":62},[38,1422,1423,1425,1427,1429],{"class":40,"line":163},[38,1424,319],{"class":97},[38,1426,322],{"class":48},[38,1428,240],{"class":44},[38,1430,327],{"class":48},[38,1432,1433],{"class":40,"line":169},[38,1434,1435],{"class":69},"    \u002F\u002F Update the chart when data changes (separate from initialization)\n",[38,1437,1438,1440],{"class":40,"line":175},[38,1439,492],{"class":44},[38,1441,1442],{"class":48}," (chartRef.current) {\n",[38,1444,1445,1448,1450],{"class":40,"line":180},[38,1446,1447],{"class":48},"      chartRef.current.data ",[38,1449,234],{"class":44},[38,1451,327],{"class":48},[38,1453,1454,1457,1459,1461,1463,1465],{"class":40,"line":186},[38,1455,1456],{"class":48},"        labels: data.",[38,1458,1357],{"class":97},[38,1460,101],{"class":48},[38,1462,1362],{"class":419},[38,1464,824],{"class":44},[38,1466,1467],{"class":48}," d.label),\n",[38,1469,1470,1473,1475,1477,1479,1481],{"class":40,"line":198},[38,1471,1472],{"class":48},"        datasets: [{ data: data.",[38,1474,1357],{"class":97},[38,1476,101],{"class":48},[38,1478,1362],{"class":419},[38,1480,824],{"class":44},[38,1482,1483],{"class":48}," d.value) }],\n",[38,1485,1486],{"class":40,"line":218},[38,1487,1488],{"class":48},"      }\n",[38,1490,1491,1494,1497],{"class":40,"line":257},[38,1492,1493],{"class":48},"      chartRef.current.",[38,1495,1496],{"class":97},"update",[38,1498,343],{"class":48},[38,1500,1501],{"class":40,"line":263},[38,1502,519],{"class":48},[38,1504,1505],{"class":40,"line":269},[38,1506,1507],{"class":48},"  }, [data])\n",[38,1509,1510],{"class":40,"line":532},[38,1511,63],{"emptyLinePlaceholder":62},[38,1513,1514,1516,1518,1521,1523,1525],{"class":40,"line":537},[38,1515,221],{"class":44},[38,1517,224],{"class":48},[38,1519,1520],{"class":227},"canvas",[38,1522,367],{"class":97},[38,1524,234],{"class":44},[38,1526,1527],{"class":48},"{canvasRef} \u002F>\n",[38,1529,1530],{"class":40,"line":575},[38,1531,272],{"class":48},[38,1533,1534],{"class":40,"line":580},[38,1535,1536],{"class":69},"\u002F\u002F Pattern: one effect for initialization (deps=[]), another for updates (deps=[data]).\n",[38,1538,1539],{"class":40,"line":586},[38,1540,1541],{"class":69},"\u002F\u002F Always destroy\u002Fdispose the library instance in the init effect's cleanup.\n",[20,1543,1545],{"id":1544},"forwardref-and-useimperativehandle","forwardRef and useImperativeHandle",[25,1547,1549],{"filename":1548,"language":28},"forward_ref.js",[30,1550,1552],{"className":32,"code":1551,"language":28,"meta":34,"style":34},"import { forwardRef, useImperativeHandle, useRef } from 'react'\n\n\u002F\u002F forwardRef: let a parent access a child's DOM node\nconst FancyInput = forwardRef(function FancyInput(props, ref) {\n  return \u003Cinput ref={ref} className=\"fancy\" {...props} \u002F>\n})\n\u002F\u002F Parent: \u003CFancyInput ref={inputRef} \u002F> → inputRef.current = the \u003Cinput> DOM node\n\n\u002F\u002F useImperativeHandle: expose a CONTROLLED API instead of the raw DOM node\nconst ControlledInput = forwardRef(function ControlledInput(props, ref) {\n  const inputRef = useRef(null)\n\n  useImperativeHandle(ref, () => ({\n    \u002F\u002F Only expose these methods to the parent — not the raw DOM node\n    focus: () => inputRef.current?.focus(),\n    clear: () => { if (inputRef.current) inputRef.current.value = '' },\n    getValue: () => inputRef.current?.value,\n    \u002F\u002F The parent CANNOT access inputRef.current directly — only these methods.\n  }), [])  \u002F\u002F empty deps → the handle is created once\n\n  return \u003Cinput ref={inputRef} {...props} \u002F>\n})\n\n\u002F\u002F Parent usage:\nfunction Form() {\n  const inputRef = useRef(null)\n  return (\n    \u003C>\n      \u003CControlledInput ref={inputRef} \u002F>\n      \u003Cbutton onClick={() => inputRef.current?.focus()}>Focus\u003C\u002Fbutton>\n      \u003Cbutton onClick={() => inputRef.current?.clear()}>Clear\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n",[15,1553,1554,1565,1569,1574,1605,1636,1641,1646,1650,1655,1682,1698,1702,1715,1720,1738,1764,1776,1781,1789,1793,1812,1816,1820,1825,1834,1850,1856,1861,1875,1900,1926,1931,1935],{"__ignoreMap":34},[38,1555,1556,1558,1561,1563],{"class":40,"line":41},[38,1557,45],{"class":44},[38,1559,1560],{"class":48}," { forwardRef, useImperativeHandle, useRef } ",[38,1562,52],{"class":44},[38,1564,56],{"class":55},[38,1566,1567],{"class":40,"line":59},[38,1568,63],{"emptyLinePlaceholder":62},[38,1570,1571],{"class":40,"line":66},[38,1572,1573],{"class":69},"\u002F\u002F forwardRef: let a parent access a child's DOM node\n",[38,1575,1576,1578,1581,1583,1586,1588,1590,1592,1594,1597,1599,1602],{"class":40,"line":73},[38,1577,87],{"class":44},[38,1579,1580],{"class":90}," FancyInput",[38,1582,94],{"class":44},[38,1584,1585],{"class":97}," forwardRef",[38,1587,101],{"class":48},[38,1589,189],{"class":44},[38,1591,1580],{"class":97},[38,1593,101],{"class":48},[38,1595,1596],{"class":419},"props",[38,1598,436],{"class":48},[38,1600,1601],{"class":419},"ref",[38,1603,1604],{"class":48},") {\n",[38,1606,1607,1609,1611,1613,1615,1617,1620,1623,1625,1628,1631,1633],{"class":40,"line":79},[38,1608,221],{"class":44},[38,1610,224],{"class":48},[38,1612,364],{"class":227},[38,1614,367],{"class":97},[38,1616,234],{"class":44},[38,1618,1619],{"class":48},"{ref} ",[38,1621,1622],{"class":97},"className",[38,1624,234],{"class":44},[38,1626,1627],{"class":55},"\"fancy\"",[38,1629,1630],{"class":48}," {",[38,1632,1196],{"class":44},[38,1634,1635],{"class":48},"props} \u002F>\n",[38,1637,1638],{"class":40,"line":84},[38,1639,1640],{"class":48},"})\n",[38,1642,1643],{"class":40,"line":110},[38,1644,1645],{"class":69},"\u002F\u002F Parent: \u003CFancyInput ref={inputRef} \u002F> → inputRef.current = the \u003Cinput> DOM node\n",[38,1647,1648],{"class":40,"line":116},[38,1649,63],{"emptyLinePlaceholder":62},[38,1651,1652],{"class":40,"line":122},[38,1653,1654],{"class":69},"\u002F\u002F useImperativeHandle: expose a CONTROLLED API instead of the raw DOM node\n",[38,1656,1657,1659,1662,1664,1666,1668,1670,1672,1674,1676,1678,1680],{"class":40,"line":127},[38,1658,87],{"class":44},[38,1660,1661],{"class":90}," ControlledInput",[38,1663,94],{"class":44},[38,1665,1585],{"class":97},[38,1667,101],{"class":48},[38,1669,189],{"class":44},[38,1671,1661],{"class":97},[38,1673,101],{"class":48},[38,1675,1596],{"class":419},[38,1677,436],{"class":48},[38,1679,1601],{"class":419},[38,1681,1604],{"class":48},[38,1683,1684,1686,1688,1690,1692,1694,1696],{"class":40,"line":133},[38,1685,201],{"class":44},[38,1687,300],{"class":90},[38,1689,94],{"class":44},[38,1691,98],{"class":97},[38,1693,101],{"class":48},[38,1695,104],{"class":90},[38,1697,107],{"class":48},[38,1699,1700],{"class":40,"line":139},[38,1701,63],{"emptyLinePlaceholder":62},[38,1703,1704,1707,1710,1712],{"class":40,"line":145},[38,1705,1706],{"class":97},"  useImperativeHandle",[38,1708,1709],{"class":48},"(ref, () ",[38,1711,240],{"class":44},[38,1713,1714],{"class":48}," ({\n",[38,1716,1717],{"class":40,"line":151},[38,1718,1719],{"class":69},"    \u002F\u002F Only expose these methods to the parent — not the raw DOM node\n",[38,1721,1722,1725,1728,1730,1733,1735],{"class":40,"line":157},[38,1723,1724],{"class":97},"    focus",[38,1726,1727],{"class":48},": () ",[38,1729,240],{"class":44},[38,1731,1732],{"class":48}," inputRef.current?.",[38,1734,340],{"class":97},[38,1736,1737],{"class":48},"(),\n",[38,1739,1740,1743,1745,1747,1750,1753,1756,1758,1761],{"class":40,"line":163},[38,1741,1742],{"class":97},"    clear",[38,1744,1727],{"class":48},[38,1746,240],{"class":44},[38,1748,1749],{"class":48}," { ",[38,1751,1752],{"class":44},"if",[38,1754,1755],{"class":48}," (inputRef.current) inputRef.current.value ",[38,1757,234],{"class":44},[38,1759,1760],{"class":55}," ''",[38,1762,1763],{"class":48}," },\n",[38,1765,1766,1769,1771,1773],{"class":40,"line":169},[38,1767,1768],{"class":97},"    getValue",[38,1770,1727],{"class":48},[38,1772,240],{"class":44},[38,1774,1775],{"class":48}," inputRef.current?.value,\n",[38,1777,1778],{"class":40,"line":175},[38,1779,1780],{"class":69},"    \u002F\u002F The parent CANNOT access inputRef.current directly — only these methods.\n",[38,1782,1783,1786],{"class":40,"line":180},[38,1784,1785],{"class":48},"  }), [])  ",[38,1787,1788],{"class":69},"\u002F\u002F empty deps → the handle is created once\n",[38,1790,1791],{"class":40,"line":186},[38,1792,63],{"emptyLinePlaceholder":62},[38,1794,1795,1797,1799,1801,1803,1805,1808,1810],{"class":40,"line":198},[38,1796,221],{"class":44},[38,1798,224],{"class":48},[38,1800,364],{"class":227},[38,1802,367],{"class":97},[38,1804,234],{"class":44},[38,1806,1807],{"class":48},"{inputRef} {",[38,1809,1196],{"class":44},[38,1811,1635],{"class":48},[38,1813,1814],{"class":40,"line":218},[38,1815,1640],{"class":48},[38,1817,1818],{"class":40,"line":257},[38,1819,63],{"emptyLinePlaceholder":62},[38,1821,1822],{"class":40,"line":263},[38,1823,1824],{"class":69},"\u002F\u002F Parent usage:\n",[38,1826,1827,1829,1832],{"class":40,"line":269},[38,1828,189],{"class":44},[38,1830,1831],{"class":97}," Form",[38,1833,195],{"class":48},[38,1835,1836,1838,1840,1842,1844,1846,1848],{"class":40,"line":532},[38,1837,201],{"class":44},[38,1839,300],{"class":90},[38,1841,94],{"class":44},[38,1843,98],{"class":97},[38,1845,101],{"class":48},[38,1847,104],{"class":90},[38,1849,107],{"class":48},[38,1851,1852,1854],{"class":40,"line":537},[38,1853,221],{"class":44},[38,1855,758],{"class":48},[38,1857,1858],{"class":40,"line":575},[38,1859,1860],{"class":48},"    \u003C>\n",[38,1862,1863,1865,1868,1870,1872],{"class":40,"line":580},[38,1864,785],{"class":48},[38,1866,1867],{"class":90},"ControlledInput",[38,1869,367],{"class":97},[38,1871,234],{"class":44},[38,1873,1874],{"class":48},"{inputRef} \u002F>\n",[38,1876,1877,1879,1881,1883,1885,1887,1889,1891,1893,1896,1898],{"class":40,"line":586},[38,1878,785],{"class":48},[38,1880,228],{"class":227},[38,1882,231],{"class":97},[38,1884,234],{"class":44},[38,1886,237],{"class":48},[38,1888,240],{"class":44},[38,1890,1732],{"class":48},[38,1892,340],{"class":97},[38,1894,1895],{"class":48},"()}>Focus\u003C\u002F",[38,1897,228],{"class":227},[38,1899,254],{"class":48},[38,1901,1902,1904,1906,1908,1910,1912,1914,1916,1919,1922,1924],{"class":40,"line":1215},[38,1903,785],{"class":48},[38,1905,228],{"class":227},[38,1907,231],{"class":97},[38,1909,234],{"class":44},[38,1911,237],{"class":48},[38,1913,240],{"class":44},[38,1915,1732],{"class":48},[38,1917,1918],{"class":97},"clear",[38,1920,1921],{"class":48},"()}>Clear\u003C\u002F",[38,1923,228],{"class":227},[38,1925,254],{"class":48},[38,1927,1928],{"class":40,"line":1221},[38,1929,1930],{"class":48},"    \u003C\u002F>\n",[38,1932,1933],{"class":40,"line":1226},[38,1934,1218],{"class":48},[38,1936,1937],{"class":40,"line":1232},[38,1938,272],{"class":48},[20,1940,1942],{"id":1941},"tips-tricks","💡 Tips & Tricks",[25,1944,1946],{"filename":1945,"language":28},"tips.js",[30,1947,1949],{"className":32,"code":1948,"language":28,"meta":34,"style":34},"\u002F\u002F [Idiom] Use useRef for \"instance variables\" — values that persist across\n\u002F\u002F renders but shouldn't trigger re-renders: timer IDs, previous prop values,\n\u002F\u002F \"is mounted\" flags, cache for expensive computations.\n\n\u002F\u002F [Debug] ref.current is null during the first render and in SSR. It's only\n\u002F\u002F populated after the DOM commits. Always guard: ref.current?.focus().\n\n\u002F\u002F [Idiom] Use callback refs when you need to react to the DOM node being\n\u002F\u002F attached\u002Fdetached — not just access it on mount. node => { if (node) ... }\n\n\u002F\u002F [Performance] useLayoutEffect blocks paint — use it ONLY for measurements\n\u002F\u002F that would flicker if done in useEffect. For all other side effects, use\n\u002F\u002F useEffect (non-blocking, runs after paint).\n\n\u002F\u002F [Idiom] When integrating third-party libs (Chart.js, Google Maps, CodeMirror),\n\u002F\u002F always destroy\u002Fdispose the instance in the effect cleanup. Leaked instances\n\u002F\u002F accumulate and cause memory issues in long-running sessions.\n",[15,1950,1951,1956,1961,1966,1970,1975,1980,1984,1989,1994,1998,2003,2008,2013,2017,2022,2027],{"__ignoreMap":34},[38,1952,1953],{"class":40,"line":41},[38,1954,1955],{"class":69},"\u002F\u002F [Idiom] Use useRef for \"instance variables\" — values that persist across\n",[38,1957,1958],{"class":40,"line":59},[38,1959,1960],{"class":69},"\u002F\u002F renders but shouldn't trigger re-renders: timer IDs, previous prop values,\n",[38,1962,1963],{"class":40,"line":66},[38,1964,1965],{"class":69},"\u002F\u002F \"is mounted\" flags, cache for expensive computations.\n",[38,1967,1968],{"class":40,"line":73},[38,1969,63],{"emptyLinePlaceholder":62},[38,1971,1972],{"class":40,"line":79},[38,1973,1974],{"class":69},"\u002F\u002F [Debug] ref.current is null during the first render and in SSR. It's only\n",[38,1976,1977],{"class":40,"line":84},[38,1978,1979],{"class":69},"\u002F\u002F populated after the DOM commits. Always guard: ref.current?.focus().\n",[38,1981,1982],{"class":40,"line":110},[38,1983,63],{"emptyLinePlaceholder":62},[38,1985,1986],{"class":40,"line":116},[38,1987,1988],{"class":69},"\u002F\u002F [Idiom] Use callback refs when you need to react to the DOM node being\n",[38,1990,1991],{"class":40,"line":122},[38,1992,1993],{"class":69},"\u002F\u002F attached\u002Fdetached — not just access it on mount. node => { if (node) ... }\n",[38,1995,1996],{"class":40,"line":127},[38,1997,63],{"emptyLinePlaceholder":62},[38,1999,2000],{"class":40,"line":133},[38,2001,2002],{"class":69},"\u002F\u002F [Performance] useLayoutEffect blocks paint — use it ONLY for measurements\n",[38,2004,2005],{"class":40,"line":139},[38,2006,2007],{"class":69},"\u002F\u002F that would flicker if done in useEffect. For all other side effects, use\n",[38,2009,2010],{"class":40,"line":145},[38,2011,2012],{"class":69},"\u002F\u002F useEffect (non-blocking, runs after paint).\n",[38,2014,2015],{"class":40,"line":151},[38,2016,63],{"emptyLinePlaceholder":62},[38,2018,2019],{"class":40,"line":157},[38,2020,2021],{"class":69},"\u002F\u002F [Idiom] When integrating third-party libs (Chart.js, Google Maps, CodeMirror),\n",[38,2023,2024],{"class":40,"line":163},[38,2025,2026],{"class":69},"\u002F\u002F always destroy\u002Fdispose the instance in the effect cleanup. Leaked instances\n",[38,2028,2029],{"class":40,"line":169},[38,2030,2031],{"class":69},"\u002F\u002F accumulate and cause memory issues in long-running sessions.\n",[20,2033,2035],{"id":2034},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[25,2037,2039],{"filename":2038,"language":28},"edge_cases.js",[30,2040,2042],{"className":32,"code":2041,"language":28,"meta":34,"style":34},"\u002F\u002F [Gotcha] Don't read or write ref.current during render — it's a mutation,\n\u002F\u002F and render must be pure. Read\u002Fwrite refs only in event handlers and effects.\n\n\u002F\u002F [Gotcha] ref.current is null on the first render and during SSR. Accessing\n\u002F\u002F it in useEffect is safe (effects run after mount), but accessing it in\n\u002F\u002F useLayoutEffect during SSR throws \"document is not defined\" — use useEffect\n\u002F\u002F for SSR-compatible code, or dynamically import the component.\n\n\u002F\u002F [Gotcha] Callback refs with inline functions fire on EVERY render (new function\n\u002F\u002F reference = React detaches and re-attaches). Wrap in useCallback to prevent\n\u002F\u002F unnecessary DOM node churn: ref={useCallback(node => ..., [])}.\n\n\u002F\u002F [Gotcha] forwardRef components don't automatically forward ref to a DOM\n\u002F\u002F element — you must explicitly attach it: \u003Cinput ref={ref} \u002F>. Forgetting\n\u002F\u002F the ref attachment means the parent gets null.\n\n\u002F\u002F [Gotcha] useImperativeHandle without forwardRef has no effect — the ref\n\u002F\u002F has nowhere to attach. Always pair useImperativeHandle with forwardRef.\n",[15,2043,2044,2049,2054,2058,2063,2068,2073,2078,2082,2087,2092,2097,2101,2106,2111,2116,2120,2125],{"__ignoreMap":34},[38,2045,2046],{"class":40,"line":41},[38,2047,2048],{"class":69},"\u002F\u002F [Gotcha] Don't read or write ref.current during render — it's a mutation,\n",[38,2050,2051],{"class":40,"line":59},[38,2052,2053],{"class":69},"\u002F\u002F and render must be pure. Read\u002Fwrite refs only in event handlers and effects.\n",[38,2055,2056],{"class":40,"line":66},[38,2057,63],{"emptyLinePlaceholder":62},[38,2059,2060],{"class":40,"line":73},[38,2061,2062],{"class":69},"\u002F\u002F [Gotcha] ref.current is null on the first render and during SSR. Accessing\n",[38,2064,2065],{"class":40,"line":79},[38,2066,2067],{"class":69},"\u002F\u002F it in useEffect is safe (effects run after mount), but accessing it in\n",[38,2069,2070],{"class":40,"line":84},[38,2071,2072],{"class":69},"\u002F\u002F useLayoutEffect during SSR throws \"document is not defined\" — use useEffect\n",[38,2074,2075],{"class":40,"line":110},[38,2076,2077],{"class":69},"\u002F\u002F for SSR-compatible code, or dynamically import the component.\n",[38,2079,2080],{"class":40,"line":116},[38,2081,63],{"emptyLinePlaceholder":62},[38,2083,2084],{"class":40,"line":122},[38,2085,2086],{"class":69},"\u002F\u002F [Gotcha] Callback refs with inline functions fire on EVERY render (new function\n",[38,2088,2089],{"class":40,"line":127},[38,2090,2091],{"class":69},"\u002F\u002F reference = React detaches and re-attaches). Wrap in useCallback to prevent\n",[38,2093,2094],{"class":40,"line":133},[38,2095,2096],{"class":69},"\u002F\u002F unnecessary DOM node churn: ref={useCallback(node => ..., [])}.\n",[38,2098,2099],{"class":40,"line":139},[38,2100,63],{"emptyLinePlaceholder":62},[38,2102,2103],{"class":40,"line":145},[38,2104,2105],{"class":69},"\u002F\u002F [Gotcha] forwardRef components don't automatically forward ref to a DOM\n",[38,2107,2108],{"class":40,"line":151},[38,2109,2110],{"class":69},"\u002F\u002F element — you must explicitly attach it: \u003Cinput ref={ref} \u002F>. Forgetting\n",[38,2112,2113],{"class":40,"line":157},[38,2114,2115],{"class":69},"\u002F\u002F the ref attachment means the parent gets null.\n",[38,2117,2118],{"class":40,"line":163},[38,2119,63],{"emptyLinePlaceholder":62},[38,2121,2122],{"class":40,"line":169},[38,2123,2124],{"class":69},"\u002F\u002F [Gotcha] useImperativeHandle without forwardRef has no effect — the ref\n",[38,2126,2127],{"class":40,"line":175},[38,2128,2129],{"class":69},"\u002F\u002F has nowhere to attach. Always pair useImperativeHandle with forwardRef.\n",[20,2131,2133],{"id":2132},"spot-the-bug","🧠 Spot the Bug",[2135,2136,2137],"p",{},"A component uses a ref to track a \"previous value\" but it always shows the current value:",[25,2139,2141],{"filename":2140,"language":28},"spot_the_bug.js",[30,2142,2144],{"className":32,"code":2143,"language":28,"meta":34,"style":34},"function PriceDisplay({ price }) {\n  const prevPriceRef = useRef(price)\n\n  useEffect(() => {\n    prevPriceRef.current = price\n  }, [price])\n\n  return \u003Cdiv>Previous: {prevPriceRef.current} | Current: {price}\u003C\u002Fdiv>\n}\n",[15,2145,2146,2160,2174,2178,2188,2198,2203,2207,2222],{"__ignoreMap":34},[38,2147,2148,2150,2153,2155,2158],{"class":40,"line":41},[38,2149,189],{"class":44},[38,2151,2152],{"class":97}," PriceDisplay",[38,2154,416],{"class":48},[38,2156,2157],{"class":419},"price",[38,2159,423],{"class":48},[38,2161,2162,2164,2167,2169,2171],{"class":40,"line":59},[38,2163,201],{"class":44},[38,2165,2166],{"class":90}," prevPriceRef",[38,2168,94],{"class":44},[38,2170,98],{"class":97},[38,2172,2173],{"class":48},"(price)\n",[38,2175,2176],{"class":40,"line":66},[38,2177,63],{"emptyLinePlaceholder":62},[38,2179,2180,2182,2184,2186],{"class":40,"line":73},[38,2181,319],{"class":97},[38,2183,322],{"class":48},[38,2185,240],{"class":44},[38,2187,327],{"class":48},[38,2189,2190,2193,2195],{"class":40,"line":79},[38,2191,2192],{"class":48},"    prevPriceRef.current ",[38,2194,234],{"class":44},[38,2196,2197],{"class":48}," price\n",[38,2199,2200],{"class":40,"line":84},[38,2201,2202],{"class":48},"  }, [price])\n",[38,2204,2205],{"class":40,"line":110},[38,2206,63],{"emptyLinePlaceholder":62},[38,2208,2209,2211,2213,2215,2218,2220],{"class":40,"line":116},[38,2210,221],{"class":44},[38,2212,224],{"class":48},[38,2214,544],{"class":227},[38,2216,2217],{"class":48},">Previous: {prevPriceRef.current} | Current: {price}\u003C\u002F",[38,2219,544],{"class":227},[38,2221,254],{"class":48},[38,2223,2224],{"class":40,"line":122},[38,2225,272],{"class":48},[2227,2228,2229,2233,2248,2273,2288,2295,2360],"details",{},[2230,2231,2232],"summary",{},"Answer",[2135,2234,2235,2236,2239,2240,2244,2245,2247],{},"The effect sets ",[15,2237,2238],{},"prevPriceRef.current = price"," ",[2241,2242,2243],"strong",{},"after"," the render has already happened. On the render where ",[15,2246,2157],{}," changes from 10 to 20:",[2249,2250,2251,2266],"ol",{},[2252,2253,2254,2255,2258,2259,2262,2263,2265],"li",{},"React renders with ",[15,2256,2257],{},"price = 20"," — the JSX reads ",[15,2260,2261],{},"prevPriceRef.current"," (still 10 from the last update) and ",[15,2264,2157],{}," (20) → shows \"Previous: 10 | Current: 20\" ✓",[2252,2267,2268,2269,2272],{},"The effect runs and sets ",[15,2270,2271],{},"prevPriceRef.current = 20"," → but the DOM already shows the correct values",[2135,2274,2275,2276,2279,2280,2283,2284,2287],{},"Wait — the code actually ",[2241,2277,2278],{},"works correctly"," because the effect runs ",[2281,2282,2243],"em",{}," render. The display shows the old ref value during render, then the effect updates it for the ",[2281,2285,2286],{},"next"," render. The pattern is correct.",[2135,2289,2290,2291,2294],{},"The actual bug would be if the assignment happened ",[2241,2292,2293],{},"during render"," instead of in an effect:",[30,2296,2298],{"className":32,"code":2297,"language":28,"meta":34,"style":34},"\u002F\u002F BUG: assigning during render means prevPriceRef.current === price always\nfunction PriceDisplay({ price }) {\n  const prevPriceRef = useRef(price)\n  prevPriceRef.current = price  \u002F\u002F ← mutation during render: both show same value\n  return \u003Cdiv>Previous: {prevPriceRef.current} | Current: {price}\u003C\u002Fdiv>\n}\n",[15,2299,2300,2305,2317,2329,2342,2356],{"__ignoreMap":34},[38,2301,2302],{"class":40,"line":41},[38,2303,2304],{"class":69},"\u002F\u002F BUG: assigning during render means prevPriceRef.current === price always\n",[38,2306,2307,2309,2311,2313,2315],{"class":40,"line":59},[38,2308,189],{"class":44},[38,2310,2152],{"class":97},[38,2312,416],{"class":48},[38,2314,2157],{"class":419},[38,2316,423],{"class":48},[38,2318,2319,2321,2323,2325,2327],{"class":40,"line":66},[38,2320,201],{"class":44},[38,2322,2166],{"class":90},[38,2324,94],{"class":44},[38,2326,98],{"class":97},[38,2328,2173],{"class":48},[38,2330,2331,2334,2336,2339],{"class":40,"line":73},[38,2332,2333],{"class":48},"  prevPriceRef.current ",[38,2335,234],{"class":44},[38,2337,2338],{"class":48}," price  ",[38,2340,2341],{"class":69},"\u002F\u002F ← mutation during render: both show same value\n",[38,2343,2344,2346,2348,2350,2352,2354],{"class":40,"line":79},[38,2345,221],{"class":44},[38,2347,224],{"class":48},[38,2349,544],{"class":227},[38,2351,2217],{"class":48},[38,2353,544],{"class":227},[38,2355,254],{"class":48},[38,2357,2358],{"class":40,"line":84},[38,2359,272],{"class":48},[2135,2361,2362,2363,2366,2367,2369],{},"This is why the original code is correct — it uses ",[15,2364,2365],{},"useEffect"," to update the ref ",[2281,2368,2243],{}," render, so the \"previous\" value is genuinely the prior render's value during the current render. The ref persists across renders, and the effect schedules the update for the next cycle.",[20,2371,2373],{"id":2372},"key-takeaways","Key Takeaways",[25,2375,2377],{"filename":2376,"language":28},"key_takeaways.js",[30,2378,2380],{"className":32,"code":2379,"language":28,"meta":34,"style":34},"\u002F\u002F 1. useRef = mutable container { current: value } that persists across renders.\n\u002F\u002F    Mutating .current does NOT trigger re-render. Use for timers, flags, caches,\n\u002F\u002F    \"previous value\" trackers — NOT for values that drive the UI.\n\n\u002F\u002F 2. DOM access: attach ref={myRef} to a DOM element. React populates .current\n\u002F\u002F    after mount, sets it to null on unmount. Always guard: ref.current?.method().\n\n\u002F\u002F 3. useLayoutEffect for DOM measurements before paint (prevents flicker).\n\u002F\u002F    useEffect for everything else (non-blocking, after paint).\n\u002F\u002F    useLayoutEffect blocks rendering — use sparingly.\n\n\u002F\u002F 4. Third-party integration: initialize in useEffect([]), update in\n\u002F\u002F    useEffect([data]), destroy in cleanup. Always dispose library instances.\n\n\u002F\u002F 5. forwardRef lets parents access a child's ref. useImperativeHandle exposes\n\u002F\u002F    a controlled API instead of the raw DOM node — pair both together.\n\u002F\u002F    Don't read\u002Fwrite ref.current during render — only in handlers and effects.\n",[15,2381,2382,2387,2392,2397,2401,2406,2411,2415,2420,2425,2430,2434,2439,2444,2448,2453,2458],{"__ignoreMap":34},[38,2383,2384],{"class":40,"line":41},[38,2385,2386],{"class":69},"\u002F\u002F 1. useRef = mutable container { current: value } that persists across renders.\n",[38,2388,2389],{"class":40,"line":59},[38,2390,2391],{"class":69},"\u002F\u002F    Mutating .current does NOT trigger re-render. Use for timers, flags, caches,\n",[38,2393,2394],{"class":40,"line":66},[38,2395,2396],{"class":69},"\u002F\u002F    \"previous value\" trackers — NOT for values that drive the UI.\n",[38,2398,2399],{"class":40,"line":73},[38,2400,63],{"emptyLinePlaceholder":62},[38,2402,2403],{"class":40,"line":79},[38,2404,2405],{"class":69},"\u002F\u002F 2. DOM access: attach ref={myRef} to a DOM element. React populates .current\n",[38,2407,2408],{"class":40,"line":84},[38,2409,2410],{"class":69},"\u002F\u002F    after mount, sets it to null on unmount. Always guard: ref.current?.method().\n",[38,2412,2413],{"class":40,"line":110},[38,2414,63],{"emptyLinePlaceholder":62},[38,2416,2417],{"class":40,"line":116},[38,2418,2419],{"class":69},"\u002F\u002F 3. useLayoutEffect for DOM measurements before paint (prevents flicker).\n",[38,2421,2422],{"class":40,"line":122},[38,2423,2424],{"class":69},"\u002F\u002F    useEffect for everything else (non-blocking, after paint).\n",[38,2426,2427],{"class":40,"line":127},[38,2428,2429],{"class":69},"\u002F\u002F    useLayoutEffect blocks rendering — use sparingly.\n",[38,2431,2432],{"class":40,"line":133},[38,2433,63],{"emptyLinePlaceholder":62},[38,2435,2436],{"class":40,"line":139},[38,2437,2438],{"class":69},"\u002F\u002F 4. Third-party integration: initialize in useEffect([]), update in\n",[38,2440,2441],{"class":40,"line":145},[38,2442,2443],{"class":69},"\u002F\u002F    useEffect([data]), destroy in cleanup. Always dispose library instances.\n",[38,2445,2446],{"class":40,"line":151},[38,2447,63],{"emptyLinePlaceholder":62},[38,2449,2450],{"class":40,"line":157},[38,2451,2452],{"class":69},"\u002F\u002F 5. forwardRef lets parents access a child's ref. useImperativeHandle exposes\n",[38,2454,2455],{"class":40,"line":163},[38,2456,2457],{"class":69},"\u002F\u002F    a controlled API instead of the raw DOM node — pair both together.\n",[38,2459,2460],{"class":40,"line":169},[38,2461,2462],{"class":69},"\u002F\u002F    Don't read\u002Fwrite ref.current during render — only in handlers and effects.\n",[554,2464,2465],{},"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 .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}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 .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}",{"title":34,"searchDepth":59,"depth":59,"links":2467},[2468,2469,2470,2471,2472,2473,2474,2475,2476,2477],{"id":22,"depth":59,"text":23},{"id":275,"depth":59,"text":276},{"id":592,"depth":59,"text":593},{"id":884,"depth":59,"text":885},{"id":1244,"depth":59,"text":1245},{"id":1544,"depth":59,"text":1545},{"id":1941,"depth":59,"text":1942},{"id":2034,"depth":59,"text":2035},{"id":2132,"depth":59,"text":2133},{"id":2372,"depth":59,"text":2373},"useRef as mutable container, DOM node access, focus management, layout measurement with useLayoutEffect, third-party library integration, forwardRef and useImperativeHandle. Code-first reference for mid-to-senior React engineers.","md",{},"\u002Freact\u002F08-useref-and-dom-access",{"title":5,"description":2478},"react\u002F08-useref-and-dom-access","JUkUy3m5dkS8QtEZT384u5zjFX2TuEjgCkEtYGM_sLU",1789924652111]