[{"data":1,"prerenderedAt":7236},["ShallowReactive",2],{"page-\u002Freact\u002F18-context-and-state-management-libraries":3},{"id":4,"title":5,"body":6,"description":7229,"extension":7230,"meta":7231,"navigation":76,"path":7232,"seo":7233,"stem":7234,"__hash__":7235},"content\u002Freact\u002F18-context-and-state-management-libraries.md","18 — Context & State Management Libraries",{"type":7,"value":8,"toc":7202},"minimark",[9,13,18,400,405,704,708,1027,1031,1511,1515,1717,1721,2188,2192,2487,2491,2708,2712,3132,3136,3679,3683,4086,4090,4440,4444,4760,4764,4852,4856,5107,5111,5662,5666,5867,5871,6052,6056,6267,6271,6278,6596,7023,7027,7198],[10,11,5],"h1",{"id":12},"_18-context-state-management-libraries",[14,15,17],"h2",{"id":16},"where-context-alone-starts-to-hurt","Where Context Alone Starts to Hurt",[19,20,23],"code-wrapper",{"filename":21,"language":22},"context_ceiling.js","javascript",[24,25,29],"pre",{"className":26,"code":27,"language":22,"meta":28,"style":28},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F Context has two structural properties fine at small scale and genuinely\n\u002F\u002F limiting at large scale:\n\u002F\u002F   1. Every consumer re-renders whenever the Context's value changes — no\n\u002F\u002F      built-in way to subscribe to just PART of it.\n\u002F\u002F   2. No mechanism for reading\u002Fupdating Context outside React components\n\u002F\u002F      (in a utility function, an analytics call, a WebSocket handler).\n\nconst AppContext = createContext(null)\n\nfunction AppProvider({ children }) {\n  const [user, setUser] = useState(null)\n  const [theme, setTheme] = useState('light')\n  const [notifications, setNotifications] = useState([])\n\n  \u002F\u002F Every time ANY of these changes, the Provider re-renders with a new value\n  \u002F\u002F object → EVERY consumer re-renders, even ones that only read `theme`.\n  const value = { user, setUser, theme, setTheme, notifications, setNotifications }\n  return \u003CAppContext.Provider value={value}>{children}\u003C\u002FAppContext.Provider>\n}\n\nfunction ThemeToggle() {\n  const { theme, setTheme } = useContext(AppContext)\n  \u002F\u002F This component ONLY cares about `theme` — but it re-renders every time\n  \u002F\u002F `user` OR `notifications` changes too, since they all live in one Provider\n  \u002F\u002F value. A new notification arriving triggers a ThemeToggle re-render.\n  return \u003Cbutton onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>{theme}\u003C\u002Fbutton>\n}\n","",[30,31,32,41,47,53,59,65,71,78,106,111,130,163,192,216,221,227,233,246,270,276,281,292,317,323,329,335,395],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sdCPZ","\u002F\u002F Context has two structural properties fine at small scale and genuinely\n",[33,42,44],{"class":35,"line":43},2,[33,45,46],{"class":39},"\u002F\u002F limiting at large scale:\n",[33,48,50],{"class":35,"line":49},3,[33,51,52],{"class":39},"\u002F\u002F   1. Every consumer re-renders whenever the Context's value changes — no\n",[33,54,56],{"class":35,"line":55},4,[33,57,58],{"class":39},"\u002F\u002F      built-in way to subscribe to just PART of it.\n",[33,60,62],{"class":35,"line":61},5,[33,63,64],{"class":39},"\u002F\u002F   2. No mechanism for reading\u002Fupdating Context outside React components\n",[33,66,68],{"class":35,"line":67},6,[33,69,70],{"class":39},"\u002F\u002F      (in a utility function, an analytics call, a WebSocket handler).\n",[33,72,74],{"class":35,"line":73},7,[33,75,77],{"emptyLinePlaceholder":76},true,"\n",[33,79,81,85,89,92,96,100,103],{"class":35,"line":80},8,[33,82,84],{"class":83},"svdQ7","const",[33,86,88],{"class":87},"snvgF"," AppContext",[33,90,91],{"class":83}," =",[33,93,95],{"class":94},"sIsaT"," createContext",[33,97,99],{"class":98},"ssxIu","(",[33,101,102],{"class":87},"null",[33,104,105],{"class":98},")\n",[33,107,109],{"class":35,"line":108},9,[33,110,77],{"emptyLinePlaceholder":76},[33,112,114,117,120,123,127],{"class":35,"line":113},10,[33,115,116],{"class":83},"function",[33,118,119],{"class":94}," AppProvider",[33,121,122],{"class":98},"({ ",[33,124,126],{"class":125},"sCrzJ","children",[33,128,129],{"class":98}," }) {\n",[33,131,133,136,139,142,145,148,151,154,157,159,161],{"class":35,"line":132},11,[33,134,135],{"class":83},"  const",[33,137,138],{"class":98}," [",[33,140,141],{"class":87},"user",[33,143,144],{"class":98},", ",[33,146,147],{"class":87},"setUser",[33,149,150],{"class":98},"] ",[33,152,153],{"class":83},"=",[33,155,156],{"class":94}," useState",[33,158,99],{"class":98},[33,160,102],{"class":87},[33,162,105],{"class":98},[33,164,166,168,170,173,175,178,180,182,184,186,190],{"class":35,"line":165},12,[33,167,135],{"class":83},[33,169,138],{"class":98},[33,171,172],{"class":87},"theme",[33,174,144],{"class":98},[33,176,177],{"class":87},"setTheme",[33,179,150],{"class":98},[33,181,153],{"class":83},[33,183,156],{"class":94},[33,185,99],{"class":98},[33,187,189],{"class":188},"sJ6F3","'light'",[33,191,105],{"class":98},[33,193,195,197,199,202,204,207,209,211,213],{"class":35,"line":194},13,[33,196,135],{"class":83},[33,198,138],{"class":98},[33,200,201],{"class":87},"notifications",[33,203,144],{"class":98},[33,205,206],{"class":87},"setNotifications",[33,208,150],{"class":98},[33,210,153],{"class":83},[33,212,156],{"class":94},[33,214,215],{"class":98},"([])\n",[33,217,219],{"class":35,"line":218},14,[33,220,77],{"emptyLinePlaceholder":76},[33,222,224],{"class":35,"line":223},15,[33,225,226],{"class":39},"  \u002F\u002F Every time ANY of these changes, the Provider re-renders with a new value\n",[33,228,230],{"class":35,"line":229},16,[33,231,232],{"class":39},"  \u002F\u002F object → EVERY consumer re-renders, even ones that only read `theme`.\n",[33,234,236,238,241,243],{"class":35,"line":235},17,[33,237,135],{"class":83},[33,239,240],{"class":87}," value",[33,242,91],{"class":83},[33,244,245],{"class":98}," { user, setUser, theme, setTheme, notifications, setNotifications }\n",[33,247,249,252,255,258,260,262,265,267],{"class":35,"line":248},18,[33,250,251],{"class":83},"  return",[33,253,254],{"class":98}," \u003C",[33,256,257],{"class":87},"AppContext.Provider",[33,259,240],{"class":94},[33,261,153],{"class":83},[33,263,264],{"class":98},"{value}>{children}\u003C\u002F",[33,266,257],{"class":87},[33,268,269],{"class":98},">\n",[33,271,273],{"class":35,"line":272},19,[33,274,275],{"class":98},"}\n",[33,277,279],{"class":35,"line":278},20,[33,280,77],{"emptyLinePlaceholder":76},[33,282,284,286,289],{"class":35,"line":283},21,[33,285,116],{"class":83},[33,287,288],{"class":94}," ThemeToggle",[33,290,291],{"class":98},"() {\n",[33,293,295,297,300,302,304,306,309,311,314],{"class":35,"line":294},22,[33,296,135],{"class":83},[33,298,299],{"class":98}," { ",[33,301,172],{"class":87},[33,303,144],{"class":98},[33,305,177],{"class":87},[33,307,308],{"class":98}," } ",[33,310,153],{"class":83},[33,312,313],{"class":94}," useContext",[33,315,316],{"class":98},"(AppContext)\n",[33,318,320],{"class":35,"line":319},23,[33,321,322],{"class":39},"  \u002F\u002F This component ONLY cares about `theme` — but it re-renders every time\n",[33,324,326],{"class":35,"line":325},24,[33,327,328],{"class":39},"  \u002F\u002F `user` OR `notifications` changes too, since they all live in one Provider\n",[33,330,332],{"class":35,"line":331},25,[33,333,334],{"class":39},"  \u002F\u002F value. A new notification arriving triggers a ThemeToggle re-render.\n",[33,336,338,340,342,346,349,351,354,357,360,362,365,368,371,374,377,380,383,386,388,391,393],{"class":35,"line":337},26,[33,339,251],{"class":83},[33,341,254],{"class":98},[33,343,345],{"class":344},"sk71V","button",[33,347,348],{"class":94}," onClick",[33,350,153],{"class":83},[33,352,353],{"class":98},"{() ",[33,355,356],{"class":83},"=>",[33,358,359],{"class":94}," setTheme",[33,361,99],{"class":98},[33,363,364],{"class":125},"t",[33,366,367],{"class":83}," =>",[33,369,370],{"class":98}," t ",[33,372,373],{"class":83},"===",[33,375,376],{"class":188}," 'light'",[33,378,379],{"class":83}," ?",[33,381,382],{"class":188}," 'dark'",[33,384,385],{"class":83}," :",[33,387,376],{"class":188},[33,389,390],{"class":98},")}>{theme}\u003C\u002F",[33,392,345],{"class":344},[33,394,269],{"class":98},[33,396,398],{"class":35,"line":397},27,[33,399,275],{"class":98},[401,402,404],"h3",{"id":403},"fix-1-split-into-multiple-contexts","Fix 1: Split Into Multiple Contexts",[19,406,408],{"filename":407,"language":22},"split_contexts.js",[24,409,411],{"className":26,"code":410,"language":22,"meta":28,"style":28},"\u002F\u002F The FIRST fix before reaching for a library: split one large Context into\n\u002F\u002F several narrowly-scoped ones. This alone fixes the \"every consumer re-renders\n\u002F\u002F on any change\" problem for many apps, at zero added dependencies.\n\nconst ThemeContext = createContext(null)\nconst UserContext = createContext(null)\nconst NotificationContext = createContext(null)\n\nfunction AppProvider({ children }) {\n  return (\n    \u003CThemeContext.Provider value={useState('light')}>\n      \u003CUserContext.Provider value={useState(null)}>\n        \u003CNotificationContext.Provider value={useState([])}>\n          {children}\n        \u003C\u002FNotificationContext.Provider>\n      \u003C\u002FUserContext.Provider>\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\nfunction ThemeToggle() {\n  const [theme, setTheme] = useContext(ThemeContext)\n  \u002F\u002F Now ONLY re-renders when theme changes — user and notification changes\n  \u002F\u002F don't affect this component at all.\n  return \u003Cbutton onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>{theme}\u003C\u002Fbutton>\n}\n",[30,412,413,418,423,428,432,449,466,483,487,499,506,531,553,572,577,586,595,604,609,613,617,625,646,651,656,700],{"__ignoreMap":28},[33,414,415],{"class":35,"line":36},[33,416,417],{"class":39},"\u002F\u002F The FIRST fix before reaching for a library: split one large Context into\n",[33,419,420],{"class":35,"line":43},[33,421,422],{"class":39},"\u002F\u002F several narrowly-scoped ones. This alone fixes the \"every consumer re-renders\n",[33,424,425],{"class":35,"line":49},[33,426,427],{"class":39},"\u002F\u002F on any change\" problem for many apps, at zero added dependencies.\n",[33,429,430],{"class":35,"line":55},[33,431,77],{"emptyLinePlaceholder":76},[33,433,434,436,439,441,443,445,447],{"class":35,"line":61},[33,435,84],{"class":83},[33,437,438],{"class":87}," ThemeContext",[33,440,91],{"class":83},[33,442,95],{"class":94},[33,444,99],{"class":98},[33,446,102],{"class":87},[33,448,105],{"class":98},[33,450,451,453,456,458,460,462,464],{"class":35,"line":67},[33,452,84],{"class":83},[33,454,455],{"class":87}," UserContext",[33,457,91],{"class":83},[33,459,95],{"class":94},[33,461,99],{"class":98},[33,463,102],{"class":87},[33,465,105],{"class":98},[33,467,468,470,473,475,477,479,481],{"class":35,"line":73},[33,469,84],{"class":83},[33,471,472],{"class":87}," NotificationContext",[33,474,91],{"class":83},[33,476,95],{"class":94},[33,478,99],{"class":98},[33,480,102],{"class":87},[33,482,105],{"class":98},[33,484,485],{"class":35,"line":80},[33,486,77],{"emptyLinePlaceholder":76},[33,488,489,491,493,495,497],{"class":35,"line":108},[33,490,116],{"class":83},[33,492,119],{"class":94},[33,494,122],{"class":98},[33,496,126],{"class":125},[33,498,129],{"class":98},[33,500,501,503],{"class":35,"line":113},[33,502,251],{"class":83},[33,504,505],{"class":98}," (\n",[33,507,508,511,514,516,518,521,524,526,528],{"class":35,"line":132},[33,509,510],{"class":98},"    \u003C",[33,512,513],{"class":87},"ThemeContext.Provider",[33,515,240],{"class":94},[33,517,153],{"class":83},[33,519,520],{"class":98},"{",[33,522,523],{"class":94},"useState",[33,525,99],{"class":98},[33,527,189],{"class":188},[33,529,530],{"class":98},")}>\n",[33,532,533,536,539,541,543,545,547,549,551],{"class":35,"line":165},[33,534,535],{"class":98},"      \u003C",[33,537,538],{"class":87},"UserContext.Provider",[33,540,240],{"class":94},[33,542,153],{"class":83},[33,544,520],{"class":98},[33,546,523],{"class":94},[33,548,99],{"class":98},[33,550,102],{"class":87},[33,552,530],{"class":98},[33,554,555,558,561,563,565,567,569],{"class":35,"line":194},[33,556,557],{"class":98},"        \u003C",[33,559,560],{"class":87},"NotificationContext.Provider",[33,562,240],{"class":94},[33,564,153],{"class":83},[33,566,520],{"class":98},[33,568,523],{"class":94},[33,570,571],{"class":98},"([])}>\n",[33,573,574],{"class":35,"line":218},[33,575,576],{"class":98},"          {children}\n",[33,578,579,582,584],{"class":35,"line":223},[33,580,581],{"class":98},"        \u003C\u002F",[33,583,560],{"class":87},[33,585,269],{"class":98},[33,587,588,591,593],{"class":35,"line":229},[33,589,590],{"class":98},"      \u003C\u002F",[33,592,538],{"class":87},[33,594,269],{"class":98},[33,596,597,600,602],{"class":35,"line":235},[33,598,599],{"class":98},"    \u003C\u002F",[33,601,513],{"class":87},[33,603,269],{"class":98},[33,605,606],{"class":35,"line":248},[33,607,608],{"class":98},"  )\n",[33,610,611],{"class":35,"line":272},[33,612,275],{"class":98},[33,614,615],{"class":35,"line":278},[33,616,77],{"emptyLinePlaceholder":76},[33,618,619,621,623],{"class":35,"line":283},[33,620,116],{"class":83},[33,622,288],{"class":94},[33,624,291],{"class":98},[33,626,627,629,631,633,635,637,639,641,643],{"class":35,"line":294},[33,628,135],{"class":83},[33,630,138],{"class":98},[33,632,172],{"class":87},[33,634,144],{"class":98},[33,636,177],{"class":87},[33,638,150],{"class":98},[33,640,153],{"class":83},[33,642,313],{"class":94},[33,644,645],{"class":98},"(ThemeContext)\n",[33,647,648],{"class":35,"line":319},[33,649,650],{"class":39},"  \u002F\u002F Now ONLY re-renders when theme changes — user and notification changes\n",[33,652,653],{"class":35,"line":325},[33,654,655],{"class":39},"  \u002F\u002F don't affect this component at all.\n",[33,657,658,660,662,664,666,668,670,672,674,676,678,680,682,684,686,688,690,692,694,696,698],{"class":35,"line":331},[33,659,251],{"class":83},[33,661,254],{"class":98},[33,663,345],{"class":344},[33,665,348],{"class":94},[33,667,153],{"class":83},[33,669,353],{"class":98},[33,671,356],{"class":83},[33,673,359],{"class":94},[33,675,99],{"class":98},[33,677,364],{"class":125},[33,679,367],{"class":83},[33,681,370],{"class":98},[33,683,373],{"class":83},[33,685,376],{"class":188},[33,687,379],{"class":83},[33,689,382],{"class":188},[33,691,385],{"class":83},[33,693,376],{"class":188},[33,695,390],{"class":98},[33,697,345],{"class":344},[33,699,269],{"class":98},[33,701,702],{"class":35,"line":337},[33,703,275],{"class":98},[401,705,707],{"id":706},"fix-2-split-value-setter-contexts","Fix 2: Split Value + Setter Contexts",[19,709,711],{"filename":710,"language":22},"value_setter_split.js",[24,712,714],{"className":26,"code":713,"language":22,"meta":28,"style":28},"\u002F\u002F Even within a single domain, the VALUE changes frequently but the SETTER\n\u002F\u002F never changes (it's a stable function). Splitting them means components that\n\u002F\u002F only use the setter don't re-render when the value changes.\n\nconst ThemeValueContext = createContext('light')\nconst ThemeSetterContext = createContext(() => {})\n\nfunction ThemeProvider({ children }) {\n  const [theme, setTheme] = useState('light')\n  \u002F\u002F setTheme from useState is stable — it never changes between renders.\n  \u002F\u002F So ThemeSetterContext consumers never re-render.\n  return (\n    \u003CThemeValueContext.Provider value={theme}>\n      \u003CThemeSetterContext.Provider value={setTheme}>\n        {children}\n      \u003C\u002FThemeSetterContext.Provider>\n    \u003C\u002FThemeValueContext.Provider>\n  )\n}\n\n\u002F\u002F A component that only SETS the theme (never reads it) won't re-render on\n\u002F\u002F theme changes:\nfunction DarkModeToggle() {\n  const setTheme = useContext(ThemeSetterContext)\n  return \u003Cbutton onClick={() => setTheme('dark')}>Go Dark\u003C\u002Fbutton>\n}\n\n\u002F\u002F A component that READS the theme re-renders only when theme changes:\nfunction ThemedCard() {\n  const theme = useContext(ThemeValueContext)\n  return \u003Cdiv className={`card card-${theme}`}>Content\u003C\u002Fdiv>\n}\n",[30,715,716,721,726,731,735,752,771,775,788,812,817,822,828,842,856,861,869,877,881,885,889,894,899,908,921,951,955,959,965,975,990,1022],{"__ignoreMap":28},[33,717,718],{"class":35,"line":36},[33,719,720],{"class":39},"\u002F\u002F Even within a single domain, the VALUE changes frequently but the SETTER\n",[33,722,723],{"class":35,"line":43},[33,724,725],{"class":39},"\u002F\u002F never changes (it's a stable function). Splitting them means components that\n",[33,727,728],{"class":35,"line":49},[33,729,730],{"class":39},"\u002F\u002F only use the setter don't re-render when the value changes.\n",[33,732,733],{"class":35,"line":55},[33,734,77],{"emptyLinePlaceholder":76},[33,736,737,739,742,744,746,748,750],{"class":35,"line":61},[33,738,84],{"class":83},[33,740,741],{"class":87}," ThemeValueContext",[33,743,91],{"class":83},[33,745,95],{"class":94},[33,747,99],{"class":98},[33,749,189],{"class":188},[33,751,105],{"class":98},[33,753,754,756,759,761,763,766,768],{"class":35,"line":67},[33,755,84],{"class":83},[33,757,758],{"class":87}," ThemeSetterContext",[33,760,91],{"class":83},[33,762,95],{"class":94},[33,764,765],{"class":98},"(() ",[33,767,356],{"class":83},[33,769,770],{"class":98}," {})\n",[33,772,773],{"class":35,"line":73},[33,774,77],{"emptyLinePlaceholder":76},[33,776,777,779,782,784,786],{"class":35,"line":80},[33,778,116],{"class":83},[33,780,781],{"class":94}," ThemeProvider",[33,783,122],{"class":98},[33,785,126],{"class":125},[33,787,129],{"class":98},[33,789,790,792,794,796,798,800,802,804,806,808,810],{"class":35,"line":108},[33,791,135],{"class":83},[33,793,138],{"class":98},[33,795,172],{"class":87},[33,797,144],{"class":98},[33,799,177],{"class":87},[33,801,150],{"class":98},[33,803,153],{"class":83},[33,805,156],{"class":94},[33,807,99],{"class":98},[33,809,189],{"class":188},[33,811,105],{"class":98},[33,813,814],{"class":35,"line":113},[33,815,816],{"class":39},"  \u002F\u002F setTheme from useState is stable — it never changes between renders.\n",[33,818,819],{"class":35,"line":132},[33,820,821],{"class":39},"  \u002F\u002F So ThemeSetterContext consumers never re-render.\n",[33,823,824,826],{"class":35,"line":165},[33,825,251],{"class":83},[33,827,505],{"class":98},[33,829,830,832,835,837,839],{"class":35,"line":194},[33,831,510],{"class":98},[33,833,834],{"class":87},"ThemeValueContext.Provider",[33,836,240],{"class":94},[33,838,153],{"class":83},[33,840,841],{"class":98},"{theme}>\n",[33,843,844,846,849,851,853],{"class":35,"line":218},[33,845,535],{"class":98},[33,847,848],{"class":87},"ThemeSetterContext.Provider",[33,850,240],{"class":94},[33,852,153],{"class":83},[33,854,855],{"class":98},"{setTheme}>\n",[33,857,858],{"class":35,"line":223},[33,859,860],{"class":98},"        {children}\n",[33,862,863,865,867],{"class":35,"line":229},[33,864,590],{"class":98},[33,866,848],{"class":87},[33,868,269],{"class":98},[33,870,871,873,875],{"class":35,"line":235},[33,872,599],{"class":98},[33,874,834],{"class":87},[33,876,269],{"class":98},[33,878,879],{"class":35,"line":248},[33,880,608],{"class":98},[33,882,883],{"class":35,"line":272},[33,884,275],{"class":98},[33,886,887],{"class":35,"line":278},[33,888,77],{"emptyLinePlaceholder":76},[33,890,891],{"class":35,"line":283},[33,892,893],{"class":39},"\u002F\u002F A component that only SETS the theme (never reads it) won't re-render on\n",[33,895,896],{"class":35,"line":294},[33,897,898],{"class":39},"\u002F\u002F theme changes:\n",[33,900,901,903,906],{"class":35,"line":319},[33,902,116],{"class":83},[33,904,905],{"class":94}," DarkModeToggle",[33,907,291],{"class":98},[33,909,910,912,914,916,918],{"class":35,"line":325},[33,911,135],{"class":83},[33,913,359],{"class":87},[33,915,91],{"class":83},[33,917,313],{"class":94},[33,919,920],{"class":98},"(ThemeSetterContext)\n",[33,922,923,925,927,929,931,933,935,937,939,941,944,947,949],{"class":35,"line":331},[33,924,251],{"class":83},[33,926,254],{"class":98},[33,928,345],{"class":344},[33,930,348],{"class":94},[33,932,153],{"class":83},[33,934,353],{"class":98},[33,936,356],{"class":83},[33,938,359],{"class":94},[33,940,99],{"class":98},[33,942,943],{"class":188},"'dark'",[33,945,946],{"class":98},")}>Go Dark\u003C\u002F",[33,948,345],{"class":344},[33,950,269],{"class":98},[33,952,953],{"class":35,"line":337},[33,954,275],{"class":98},[33,956,957],{"class":35,"line":397},[33,958,77],{"emptyLinePlaceholder":76},[33,960,962],{"class":35,"line":961},28,[33,963,964],{"class":39},"\u002F\u002F A component that READS the theme re-renders only when theme changes:\n",[33,966,968,970,973],{"class":35,"line":967},29,[33,969,116],{"class":83},[33,971,972],{"class":94}," ThemedCard",[33,974,291],{"class":98},[33,976,978,980,983,985,987],{"class":35,"line":977},30,[33,979,135],{"class":83},[33,981,982],{"class":87}," theme",[33,984,91],{"class":83},[33,986,313],{"class":94},[33,988,989],{"class":98},"(ThemeValueContext)\n",[33,991,993,995,997,1000,1003,1005,1007,1010,1012,1015,1018,1020],{"class":35,"line":992},31,[33,994,251],{"class":83},[33,996,254],{"class":98},[33,998,999],{"class":344},"div",[33,1001,1002],{"class":94}," className",[33,1004,153],{"class":83},[33,1006,520],{"class":98},[33,1008,1009],{"class":188},"`card card-${",[33,1011,172],{"class":98},[33,1013,1014],{"class":188},"}`",[33,1016,1017],{"class":98},"}>Content\u003C\u002F",[33,1019,999],{"class":344},[33,1021,269],{"class":98},[33,1023,1025],{"class":35,"line":1024},32,[33,1026,275],{"class":98},[401,1028,1030],{"id":1029},"fix-3-context-external-store-usesyncexternalstore","Fix 3: Context + External Store (useSyncExternalStore)",[19,1032,1034],{"filename":1033,"language":22},"context_external_store.js",[24,1035,1037],{"className":26,"code":1036,"language":22,"meta":28,"style":28},"\u002F\u002F For fine-grained subscriptions without a library, use useSyncExternalStore\n\u002F\u002F (React 18+) to let consumers subscribe to only the slice they need.\n\nimport { useSyncExternalStore, createContext, useContext } from 'react'\n\n\u002F\u002F An external store with subscribe + getSnapshot:\nfunction createStore(initialState) {\n  let state = initialState\n  const listeners = new Set()\n  return {\n    getState: () => state,\n    setState: (updater) => {\n      state = typeof updater === 'function' ? updater(state) : updater\n      listeners.forEach(l => l())\n    },\n    subscribe: (listener) => {\n      listeners.add(listener)\n      return () => listeners.delete(listener)\n    },\n  }\n}\n\nconst store = createStore({ user: null, theme: 'light', notifications: [] })\nconst StoreContext = createContext(null)\n\nfunction StoreProvider({ children }) {\n  return \u003CStoreContext.Provider value={store}>{children}\u003C\u002FStoreContext.Provider>\n}\n\n\u002F\u002F Consumer subscribes to a SLICE — re-renders only when that slice changes:\nfunction ThemeToggle() {\n  const store = useContext(StoreContext)\n  \u002F\u002F useSyncExternalStore subscribes to the store, but only re-renders this\n  \u002F\u002F component when the return value of getSnapshot changes (by reference).\n  const theme = useSyncExternalStore(\n    store.subscribe,\n    () => store.getState().theme,  \u002F\u002F selector — only re-renders when theme changes\n  )\n  return \u003Cbutton onClick={() => store.setState(s => ({ ...s, theme: s.theme === 'light' ? 'dark' : 'light' }))}>{theme}\u003C\u002Fbutton>\n}\n",[30,1038,1039,1044,1049,1053,1067,1071,1076,1091,1104,1122,1129,1142,1160,1192,1213,1218,1234,1244,1262,1266,1271,1275,1279,1303,1320,1324,1337,1357,1361,1365,1370,1378,1391,1397,1403,1418,1424,1444,1449,1506],{"__ignoreMap":28},[33,1040,1041],{"class":35,"line":36},[33,1042,1043],{"class":39},"\u002F\u002F For fine-grained subscriptions without a library, use useSyncExternalStore\n",[33,1045,1046],{"class":35,"line":43},[33,1047,1048],{"class":39},"\u002F\u002F (React 18+) to let consumers subscribe to only the slice they need.\n",[33,1050,1051],{"class":35,"line":49},[33,1052,77],{"emptyLinePlaceholder":76},[33,1054,1055,1058,1061,1064],{"class":35,"line":55},[33,1056,1057],{"class":83},"import",[33,1059,1060],{"class":98}," { useSyncExternalStore, createContext, useContext } ",[33,1062,1063],{"class":83},"from",[33,1065,1066],{"class":188}," 'react'\n",[33,1068,1069],{"class":35,"line":61},[33,1070,77],{"emptyLinePlaceholder":76},[33,1072,1073],{"class":35,"line":67},[33,1074,1075],{"class":39},"\u002F\u002F An external store with subscribe + getSnapshot:\n",[33,1077,1078,1080,1083,1085,1088],{"class":35,"line":73},[33,1079,116],{"class":83},[33,1081,1082],{"class":94}," createStore",[33,1084,99],{"class":98},[33,1086,1087],{"class":125},"initialState",[33,1089,1090],{"class":98},") {\n",[33,1092,1093,1096,1099,1101],{"class":35,"line":80},[33,1094,1095],{"class":83},"  let",[33,1097,1098],{"class":98}," state ",[33,1100,153],{"class":83},[33,1102,1103],{"class":98}," initialState\n",[33,1105,1106,1108,1111,1113,1116,1119],{"class":35,"line":108},[33,1107,135],{"class":83},[33,1109,1110],{"class":87}," listeners",[33,1112,91],{"class":83},[33,1114,1115],{"class":83}," new",[33,1117,1118],{"class":94}," Set",[33,1120,1121],{"class":98},"()\n",[33,1123,1124,1126],{"class":35,"line":113},[33,1125,251],{"class":83},[33,1127,1128],{"class":98}," {\n",[33,1130,1131,1134,1137,1139],{"class":35,"line":132},[33,1132,1133],{"class":94},"    getState",[33,1135,1136],{"class":98},": () ",[33,1138,356],{"class":83},[33,1140,1141],{"class":98}," state,\n",[33,1143,1144,1147,1150,1153,1156,1158],{"class":35,"line":165},[33,1145,1146],{"class":94},"    setState",[33,1148,1149],{"class":98},": (",[33,1151,1152],{"class":125},"updater",[33,1154,1155],{"class":98},") ",[33,1157,356],{"class":83},[33,1159,1128],{"class":98},[33,1161,1162,1165,1167,1170,1173,1175,1178,1180,1183,1186,1189],{"class":35,"line":194},[33,1163,1164],{"class":98},"      state ",[33,1166,153],{"class":83},[33,1168,1169],{"class":83}," typeof",[33,1171,1172],{"class":98}," updater ",[33,1174,373],{"class":83},[33,1176,1177],{"class":188}," 'function'",[33,1179,379],{"class":83},[33,1181,1182],{"class":94}," updater",[33,1184,1185],{"class":98},"(state) ",[33,1187,1188],{"class":83},":",[33,1190,1191],{"class":98}," updater\n",[33,1193,1194,1197,1200,1202,1205,1207,1210],{"class":35,"line":218},[33,1195,1196],{"class":98},"      listeners.",[33,1198,1199],{"class":94},"forEach",[33,1201,99],{"class":98},[33,1203,1204],{"class":125},"l",[33,1206,367],{"class":83},[33,1208,1209],{"class":94}," l",[33,1211,1212],{"class":98},"())\n",[33,1214,1215],{"class":35,"line":223},[33,1216,1217],{"class":98},"    },\n",[33,1219,1220,1223,1225,1228,1230,1232],{"class":35,"line":229},[33,1221,1222],{"class":94},"    subscribe",[33,1224,1149],{"class":98},[33,1226,1227],{"class":125},"listener",[33,1229,1155],{"class":98},[33,1231,356],{"class":83},[33,1233,1128],{"class":98},[33,1235,1236,1238,1241],{"class":35,"line":235},[33,1237,1196],{"class":98},[33,1239,1240],{"class":94},"add",[33,1242,1243],{"class":98},"(listener)\n",[33,1245,1246,1249,1252,1254,1257,1260],{"class":35,"line":248},[33,1247,1248],{"class":83},"      return",[33,1250,1251],{"class":98}," () ",[33,1253,356],{"class":83},[33,1255,1256],{"class":98}," listeners.",[33,1258,1259],{"class":94},"delete",[33,1261,1243],{"class":98},[33,1263,1264],{"class":35,"line":272},[33,1265,1217],{"class":98},[33,1267,1268],{"class":35,"line":278},[33,1269,1270],{"class":98},"  }\n",[33,1272,1273],{"class":35,"line":283},[33,1274,275],{"class":98},[33,1276,1277],{"class":35,"line":294},[33,1278,77],{"emptyLinePlaceholder":76},[33,1280,1281,1283,1286,1288,1290,1293,1295,1298,1300],{"class":35,"line":319},[33,1282,84],{"class":83},[33,1284,1285],{"class":87}," store",[33,1287,91],{"class":83},[33,1289,1082],{"class":94},[33,1291,1292],{"class":98},"({ user: ",[33,1294,102],{"class":87},[33,1296,1297],{"class":98},", theme: ",[33,1299,189],{"class":188},[33,1301,1302],{"class":98},", notifications: [] })\n",[33,1304,1305,1307,1310,1312,1314,1316,1318],{"class":35,"line":325},[33,1306,84],{"class":83},[33,1308,1309],{"class":87}," StoreContext",[33,1311,91],{"class":83},[33,1313,95],{"class":94},[33,1315,99],{"class":98},[33,1317,102],{"class":87},[33,1319,105],{"class":98},[33,1321,1322],{"class":35,"line":331},[33,1323,77],{"emptyLinePlaceholder":76},[33,1325,1326,1328,1331,1333,1335],{"class":35,"line":337},[33,1327,116],{"class":83},[33,1329,1330],{"class":94}," StoreProvider",[33,1332,122],{"class":98},[33,1334,126],{"class":125},[33,1336,129],{"class":98},[33,1338,1339,1341,1343,1346,1348,1350,1353,1355],{"class":35,"line":397},[33,1340,251],{"class":83},[33,1342,254],{"class":98},[33,1344,1345],{"class":87},"StoreContext.Provider",[33,1347,240],{"class":94},[33,1349,153],{"class":83},[33,1351,1352],{"class":98},"{store}>{children}\u003C\u002F",[33,1354,1345],{"class":87},[33,1356,269],{"class":98},[33,1358,1359],{"class":35,"line":961},[33,1360,275],{"class":98},[33,1362,1363],{"class":35,"line":967},[33,1364,77],{"emptyLinePlaceholder":76},[33,1366,1367],{"class":35,"line":977},[33,1368,1369],{"class":39},"\u002F\u002F Consumer subscribes to a SLICE — re-renders only when that slice changes:\n",[33,1371,1372,1374,1376],{"class":35,"line":992},[33,1373,116],{"class":83},[33,1375,288],{"class":94},[33,1377,291],{"class":98},[33,1379,1380,1382,1384,1386,1388],{"class":35,"line":1024},[33,1381,135],{"class":83},[33,1383,1285],{"class":87},[33,1385,91],{"class":83},[33,1387,313],{"class":94},[33,1389,1390],{"class":98},"(StoreContext)\n",[33,1392,1394],{"class":35,"line":1393},33,[33,1395,1396],{"class":39},"  \u002F\u002F useSyncExternalStore subscribes to the store, but only re-renders this\n",[33,1398,1400],{"class":35,"line":1399},34,[33,1401,1402],{"class":39},"  \u002F\u002F component when the return value of getSnapshot changes (by reference).\n",[33,1404,1406,1408,1410,1412,1415],{"class":35,"line":1405},35,[33,1407,135],{"class":83},[33,1409,982],{"class":87},[33,1411,91],{"class":83},[33,1413,1414],{"class":94}," useSyncExternalStore",[33,1416,1417],{"class":98},"(\n",[33,1419,1421],{"class":35,"line":1420},36,[33,1422,1423],{"class":98},"    store.subscribe,\n",[33,1425,1427,1430,1432,1435,1438,1441],{"class":35,"line":1426},37,[33,1428,1429],{"class":98},"    () ",[33,1431,356],{"class":83},[33,1433,1434],{"class":98}," store.",[33,1436,1437],{"class":94},"getState",[33,1439,1440],{"class":98},"().theme,  ",[33,1442,1443],{"class":39},"\u002F\u002F selector — only re-renders when theme changes\n",[33,1445,1447],{"class":35,"line":1446},38,[33,1448,608],{"class":98},[33,1450,1452,1454,1456,1458,1460,1462,1464,1466,1468,1471,1473,1476,1478,1481,1484,1487,1489,1491,1493,1495,1497,1499,1502,1504],{"class":35,"line":1451},39,[33,1453,251],{"class":83},[33,1455,254],{"class":98},[33,1457,345],{"class":344},[33,1459,348],{"class":94},[33,1461,153],{"class":83},[33,1463,353],{"class":98},[33,1465,356],{"class":83},[33,1467,1434],{"class":98},[33,1469,1470],{"class":94},"setState",[33,1472,99],{"class":98},[33,1474,1475],{"class":125},"s",[33,1477,367],{"class":83},[33,1479,1480],{"class":98}," ({ ",[33,1482,1483],{"class":83},"...",[33,1485,1486],{"class":98},"s, theme: s.theme ",[33,1488,373],{"class":83},[33,1490,376],{"class":188},[33,1492,379],{"class":83},[33,1494,382],{"class":188},[33,1496,385],{"class":83},[33,1498,376],{"class":188},[33,1500,1501],{"class":98}," }))}>{theme}\u003C\u002F",[33,1503,345],{"class":344},[33,1505,269],{"class":98},[33,1507,1509],{"class":35,"line":1508},40,[33,1510,275],{"class":98},[14,1512,1514],{"id":1513},"when-context-is-still-the-right-tool","When Context Is Still the Right Tool",[19,1516,1518],{"filename":1517,"language":22},"context_enough.js",[24,1519,1521],{"className":26,"code":1520,"language":22,"meta":28,"style":28},"\u002F\u002F Most applications NEVER hit the Context ceiling. Context remains the right\n\u002F\u002F default for:\n\u002F\u002F - Infrequently-changing state (theme, locale, authenticated user)\n\u002F\u002F - State read by relatively few components\n\u002F\u002F - App-wide configuration that rarely updates\n\u002F\u002F - Dependency injection (providing a service\u002Fclient to deep children)\n\n\u002F\u002F Reaching for a state library PREEMPTIVELY, before Context has caused an\n\u002F\u002F actual, measured problem, adds a dependency and a learning curve for no\n\u002F\u002F real benefit. Context + splitting is often enough.\n\n\u002F\u002F GOOD Context use cases:\nconst ThemeContext = createContext('light')           \u002F\u002F changes rarely\nconst LocaleContext = createContext('en-US')           \u002F\u002F changes rarely\nconst AuthUserContext = createContext(null)            \u002F\u002F changes on login\u002Flogout only\nconst ApiClientContext = createContext(null)           \u002F\u002F never changes (DI)\nconst RouterContext = createContext(null)              \u002F\u002F framework-provided\n\n\u002F\u002F BAD Context use cases (use a state library instead):\n\u002F\u002F - A notification system that fires updates every few seconds\n\u002F\u002F - A shopping cart that changes on every user interaction\n\u002F\u002F - Search results that update on every keystroke\n\u002F\u002F - Form state with many fields updating rapidly\n\u002F\u002F - Any state that needs to be read\u002Fupdated outside React components\n",[30,1522,1523,1528,1533,1538,1543,1548,1553,1557,1562,1567,1572,1576,1581,1601,1621,1642,1662,1683,1687,1692,1697,1702,1707,1712],{"__ignoreMap":28},[33,1524,1525],{"class":35,"line":36},[33,1526,1527],{"class":39},"\u002F\u002F Most applications NEVER hit the Context ceiling. Context remains the right\n",[33,1529,1530],{"class":35,"line":43},[33,1531,1532],{"class":39},"\u002F\u002F default for:\n",[33,1534,1535],{"class":35,"line":49},[33,1536,1537],{"class":39},"\u002F\u002F - Infrequently-changing state (theme, locale, authenticated user)\n",[33,1539,1540],{"class":35,"line":55},[33,1541,1542],{"class":39},"\u002F\u002F - State read by relatively few components\n",[33,1544,1545],{"class":35,"line":61},[33,1546,1547],{"class":39},"\u002F\u002F - App-wide configuration that rarely updates\n",[33,1549,1550],{"class":35,"line":67},[33,1551,1552],{"class":39},"\u002F\u002F - Dependency injection (providing a service\u002Fclient to deep children)\n",[33,1554,1555],{"class":35,"line":73},[33,1556,77],{"emptyLinePlaceholder":76},[33,1558,1559],{"class":35,"line":80},[33,1560,1561],{"class":39},"\u002F\u002F Reaching for a state library PREEMPTIVELY, before Context has caused an\n",[33,1563,1564],{"class":35,"line":108},[33,1565,1566],{"class":39},"\u002F\u002F actual, measured problem, adds a dependency and a learning curve for no\n",[33,1568,1569],{"class":35,"line":113},[33,1570,1571],{"class":39},"\u002F\u002F real benefit. Context + splitting is often enough.\n",[33,1573,1574],{"class":35,"line":132},[33,1575,77],{"emptyLinePlaceholder":76},[33,1577,1578],{"class":35,"line":165},[33,1579,1580],{"class":39},"\u002F\u002F GOOD Context use cases:\n",[33,1582,1583,1585,1587,1589,1591,1593,1595,1598],{"class":35,"line":194},[33,1584,84],{"class":83},[33,1586,438],{"class":87},[33,1588,91],{"class":83},[33,1590,95],{"class":94},[33,1592,99],{"class":98},[33,1594,189],{"class":188},[33,1596,1597],{"class":98},")           ",[33,1599,1600],{"class":39},"\u002F\u002F changes rarely\n",[33,1602,1603,1605,1608,1610,1612,1614,1617,1619],{"class":35,"line":218},[33,1604,84],{"class":83},[33,1606,1607],{"class":87}," LocaleContext",[33,1609,91],{"class":83},[33,1611,95],{"class":94},[33,1613,99],{"class":98},[33,1615,1616],{"class":188},"'en-US'",[33,1618,1597],{"class":98},[33,1620,1600],{"class":39},[33,1622,1623,1625,1628,1630,1632,1634,1636,1639],{"class":35,"line":223},[33,1624,84],{"class":83},[33,1626,1627],{"class":87}," AuthUserContext",[33,1629,91],{"class":83},[33,1631,95],{"class":94},[33,1633,99],{"class":98},[33,1635,102],{"class":87},[33,1637,1638],{"class":98},")            ",[33,1640,1641],{"class":39},"\u002F\u002F changes on login\u002Flogout only\n",[33,1643,1644,1646,1649,1651,1653,1655,1657,1659],{"class":35,"line":229},[33,1645,84],{"class":83},[33,1647,1648],{"class":87}," ApiClientContext",[33,1650,91],{"class":83},[33,1652,95],{"class":94},[33,1654,99],{"class":98},[33,1656,102],{"class":87},[33,1658,1597],{"class":98},[33,1660,1661],{"class":39},"\u002F\u002F never changes (DI)\n",[33,1663,1664,1666,1669,1671,1673,1675,1677,1680],{"class":35,"line":235},[33,1665,84],{"class":83},[33,1667,1668],{"class":87}," RouterContext",[33,1670,91],{"class":83},[33,1672,95],{"class":94},[33,1674,99],{"class":98},[33,1676,102],{"class":87},[33,1678,1679],{"class":98},")              ",[33,1681,1682],{"class":39},"\u002F\u002F framework-provided\n",[33,1684,1685],{"class":35,"line":248},[33,1686,77],{"emptyLinePlaceholder":76},[33,1688,1689],{"class":35,"line":272},[33,1690,1691],{"class":39},"\u002F\u002F BAD Context use cases (use a state library instead):\n",[33,1693,1694],{"class":35,"line":278},[33,1695,1696],{"class":39},"\u002F\u002F - A notification system that fires updates every few seconds\n",[33,1698,1699],{"class":35,"line":283},[33,1700,1701],{"class":39},"\u002F\u002F - A shopping cart that changes on every user interaction\n",[33,1703,1704],{"class":35,"line":294},[33,1705,1706],{"class":39},"\u002F\u002F - Search results that update on every keystroke\n",[33,1708,1709],{"class":35,"line":319},[33,1710,1711],{"class":39},"\u002F\u002F - Form state with many fields updating rapidly\n",[33,1713,1714],{"class":35,"line":325},[33,1715,1716],{"class":39},"\u002F\u002F - Any state that needs to be read\u002Fupdated outside React components\n",[14,1718,1720],{"id":1719},"zustand-minimal-hook-based-global-state","Zustand: Minimal, Hook-Based Global State",[19,1722,1724],{"filename":1723,"language":22},"zustand_basic.js",[24,1725,1727],{"className":26,"code":1726,"language":22,"meta":28,"style":28},"\u002F\u002F Zustand takes nearly the opposite approach from Redux: a store is just a\n\u002F\u002F function returning state and the functions that update it — no actions, no\n\u002F\u002F reducers, no Provider wrapping the app.\n\nimport { create } from 'zustand'\n\nconst useCartStore = create((set, get) => ({\n  items: [],\n  addItem: (item) => set(state => ({ items: [...state.items, item] })),\n  removeItem: (id) => set(state => ({ items: state.items.filter(item => item.id !== id) })),\n  clearCart: () => set({ items: [] }),\n  \u002F\u002F `get` lets you read current state without subscribing to it:\n  getTotalPrice: () => get().items.reduce((sum, item) => sum + item.price, 0),\n}))\n\n\u002F\u002F No \u003CProvider> needed anywhere — useCartStore can be imported and called\n\u002F\u002F directly from any component. The store is a singleton across the whole app.\n\nfunction CartSummary() {\n  \u002F\u002F Selector function provides fine-grained subscription — this component\n  \u002F\u002F re-renders ONLY when items.length specifically changes, not on other changes.\n  const itemCount = useCartStore(state => state.items.length)\n  const addItem = useCartStore(state => state.addItem)\n\n  return (\n    \u003Cdiv>\n      \u003Cspan>{itemCount} items\u003C\u002Fspan>\n      \u003Cbutton onClick={() => addItem({ id: 3, name: 'Widget', price: 9.99 })}>Add Widget\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F CRITICAL: Zustand state can be read and updated from OUTSIDE React entirely:\nfunction handleWebSocketMessage(message) {\n  if (message.type === 'cart_update') {\n    useCartStore.getState().addItem(message.item)  \u002F\u002F no hook, no component\n  }\n}\n\n\u002F\u002F This directly solves Context's \"can't read outside components\" limitation.\n",[30,1728,1729,1734,1739,1744,1748,1760,1764,1794,1799,1831,1874,1888,1893,1939,1944,1948,1953,1958,1962,1971,1976,1981,2006,2026,2030,2036,2044,2057,2098,2106,2110,2114,2118,2123,2137,2152,2171,2175,2179,2183],{"__ignoreMap":28},[33,1730,1731],{"class":35,"line":36},[33,1732,1733],{"class":39},"\u002F\u002F Zustand takes nearly the opposite approach from Redux: a store is just a\n",[33,1735,1736],{"class":35,"line":43},[33,1737,1738],{"class":39},"\u002F\u002F function returning state and the functions that update it — no actions, no\n",[33,1740,1741],{"class":35,"line":49},[33,1742,1743],{"class":39},"\u002F\u002F reducers, no Provider wrapping the app.\n",[33,1745,1746],{"class":35,"line":55},[33,1747,77],{"emptyLinePlaceholder":76},[33,1749,1750,1752,1755,1757],{"class":35,"line":61},[33,1751,1057],{"class":83},[33,1753,1754],{"class":98}," { create } ",[33,1756,1063],{"class":83},[33,1758,1759],{"class":188}," 'zustand'\n",[33,1761,1762],{"class":35,"line":67},[33,1763,77],{"emptyLinePlaceholder":76},[33,1765,1766,1768,1771,1773,1776,1779,1782,1784,1787,1789,1791],{"class":35,"line":73},[33,1767,84],{"class":83},[33,1769,1770],{"class":87}," useCartStore",[33,1772,91],{"class":83},[33,1774,1775],{"class":94}," create",[33,1777,1778],{"class":98},"((",[33,1780,1781],{"class":125},"set",[33,1783,144],{"class":98},[33,1785,1786],{"class":125},"get",[33,1788,1155],{"class":98},[33,1790,356],{"class":83},[33,1792,1793],{"class":98}," ({\n",[33,1795,1796],{"class":35,"line":80},[33,1797,1798],{"class":98},"  items: [],\n",[33,1800,1801,1804,1806,1809,1811,1813,1816,1818,1821,1823,1826,1828],{"class":35,"line":108},[33,1802,1803],{"class":94},"  addItem",[33,1805,1149],{"class":98},[33,1807,1808],{"class":125},"item",[33,1810,1155],{"class":98},[33,1812,356],{"class":83},[33,1814,1815],{"class":94}," set",[33,1817,99],{"class":98},[33,1819,1820],{"class":125},"state",[33,1822,367],{"class":83},[33,1824,1825],{"class":98}," ({ items: [",[33,1827,1483],{"class":83},[33,1829,1830],{"class":98},"state.items, item] })),\n",[33,1832,1833,1836,1838,1841,1843,1845,1847,1849,1851,1853,1856,1859,1861,1863,1865,1868,1871],{"class":35,"line":113},[33,1834,1835],{"class":94},"  removeItem",[33,1837,1149],{"class":98},[33,1839,1840],{"class":125},"id",[33,1842,1155],{"class":98},[33,1844,356],{"class":83},[33,1846,1815],{"class":94},[33,1848,99],{"class":98},[33,1850,1820],{"class":125},[33,1852,367],{"class":83},[33,1854,1855],{"class":98}," ({ items: state.items.",[33,1857,1858],{"class":94},"filter",[33,1860,99],{"class":98},[33,1862,1808],{"class":125},[33,1864,367],{"class":83},[33,1866,1867],{"class":98}," item.id ",[33,1869,1870],{"class":83},"!==",[33,1872,1873],{"class":98}," id) })),\n",[33,1875,1876,1879,1881,1883,1885],{"class":35,"line":132},[33,1877,1878],{"class":94},"  clearCart",[33,1880,1136],{"class":98},[33,1882,356],{"class":83},[33,1884,1815],{"class":94},[33,1886,1887],{"class":98},"({ items: [] }),\n",[33,1889,1890],{"class":35,"line":165},[33,1891,1892],{"class":39},"  \u002F\u002F `get` lets you read current state without subscribing to it:\n",[33,1894,1895,1898,1900,1902,1905,1908,1911,1913,1916,1918,1920,1922,1924,1927,1930,1933,1936],{"class":35,"line":194},[33,1896,1897],{"class":94},"  getTotalPrice",[33,1899,1136],{"class":98},[33,1901,356],{"class":83},[33,1903,1904],{"class":94}," get",[33,1906,1907],{"class":98},"().items.",[33,1909,1910],{"class":94},"reduce",[33,1912,1778],{"class":98},[33,1914,1915],{"class":125},"sum",[33,1917,144],{"class":98},[33,1919,1808],{"class":125},[33,1921,1155],{"class":98},[33,1923,356],{"class":83},[33,1925,1926],{"class":98}," sum ",[33,1928,1929],{"class":83},"+",[33,1931,1932],{"class":98}," item.price, ",[33,1934,1935],{"class":87},"0",[33,1937,1938],{"class":98},"),\n",[33,1940,1941],{"class":35,"line":218},[33,1942,1943],{"class":98},"}))\n",[33,1945,1946],{"class":35,"line":223},[33,1947,77],{"emptyLinePlaceholder":76},[33,1949,1950],{"class":35,"line":229},[33,1951,1952],{"class":39},"\u002F\u002F No \u003CProvider> needed anywhere — useCartStore can be imported and called\n",[33,1954,1955],{"class":35,"line":235},[33,1956,1957],{"class":39},"\u002F\u002F directly from any component. The store is a singleton across the whole app.\n",[33,1959,1960],{"class":35,"line":248},[33,1961,77],{"emptyLinePlaceholder":76},[33,1963,1964,1966,1969],{"class":35,"line":272},[33,1965,116],{"class":83},[33,1967,1968],{"class":94}," CartSummary",[33,1970,291],{"class":98},[33,1972,1973],{"class":35,"line":278},[33,1974,1975],{"class":39},"  \u002F\u002F Selector function provides fine-grained subscription — this component\n",[33,1977,1978],{"class":35,"line":283},[33,1979,1980],{"class":39},"  \u002F\u002F re-renders ONLY when items.length specifically changes, not on other changes.\n",[33,1982,1983,1985,1988,1990,1992,1994,1996,1998,2001,2004],{"class":35,"line":294},[33,1984,135],{"class":83},[33,1986,1987],{"class":87}," itemCount",[33,1989,91],{"class":83},[33,1991,1770],{"class":94},[33,1993,99],{"class":98},[33,1995,1820],{"class":125},[33,1997,367],{"class":83},[33,1999,2000],{"class":98}," state.items.",[33,2002,2003],{"class":87},"length",[33,2005,105],{"class":98},[33,2007,2008,2010,2013,2015,2017,2019,2021,2023],{"class":35,"line":319},[33,2009,135],{"class":83},[33,2011,2012],{"class":87}," addItem",[33,2014,91],{"class":83},[33,2016,1770],{"class":94},[33,2018,99],{"class":98},[33,2020,1820],{"class":125},[33,2022,367],{"class":83},[33,2024,2025],{"class":98}," state.addItem)\n",[33,2027,2028],{"class":35,"line":325},[33,2029,77],{"emptyLinePlaceholder":76},[33,2031,2032,2034],{"class":35,"line":331},[33,2033,251],{"class":83},[33,2035,505],{"class":98},[33,2037,2038,2040,2042],{"class":35,"line":337},[33,2039,510],{"class":98},[33,2041,999],{"class":344},[33,2043,269],{"class":98},[33,2045,2046,2048,2050,2053,2055],{"class":35,"line":397},[33,2047,535],{"class":98},[33,2049,33],{"class":344},[33,2051,2052],{"class":98},">{itemCount} items\u003C\u002F",[33,2054,33],{"class":344},[33,2056,269],{"class":98},[33,2058,2059,2061,2063,2065,2067,2069,2071,2073,2076,2079,2082,2085,2088,2091,2094,2096],{"class":35,"line":961},[33,2060,535],{"class":98},[33,2062,345],{"class":344},[33,2064,348],{"class":94},[33,2066,153],{"class":83},[33,2068,353],{"class":98},[33,2070,356],{"class":83},[33,2072,2012],{"class":94},[33,2074,2075],{"class":98},"({ id: ",[33,2077,2078],{"class":87},"3",[33,2080,2081],{"class":98},", name: ",[33,2083,2084],{"class":188},"'Widget'",[33,2086,2087],{"class":98},", price: ",[33,2089,2090],{"class":87},"9.99",[33,2092,2093],{"class":98}," })}>Add Widget\u003C\u002F",[33,2095,345],{"class":344},[33,2097,269],{"class":98},[33,2099,2100,2102,2104],{"class":35,"line":967},[33,2101,599],{"class":98},[33,2103,999],{"class":344},[33,2105,269],{"class":98},[33,2107,2108],{"class":35,"line":977},[33,2109,608],{"class":98},[33,2111,2112],{"class":35,"line":992},[33,2113,275],{"class":98},[33,2115,2116],{"class":35,"line":1024},[33,2117,77],{"emptyLinePlaceholder":76},[33,2119,2120],{"class":35,"line":1393},[33,2121,2122],{"class":39},"\u002F\u002F CRITICAL: Zustand state can be read and updated from OUTSIDE React entirely:\n",[33,2124,2125,2127,2130,2132,2135],{"class":35,"line":1399},[33,2126,116],{"class":83},[33,2128,2129],{"class":94}," handleWebSocketMessage",[33,2131,99],{"class":98},[33,2133,2134],{"class":125},"message",[33,2136,1090],{"class":98},[33,2138,2139,2142,2145,2147,2150],{"class":35,"line":1405},[33,2140,2141],{"class":83},"  if",[33,2143,2144],{"class":98}," (message.type ",[33,2146,373],{"class":83},[33,2148,2149],{"class":188}," 'cart_update'",[33,2151,1090],{"class":98},[33,2153,2154,2157,2159,2162,2165,2168],{"class":35,"line":1420},[33,2155,2156],{"class":98},"    useCartStore.",[33,2158,1437],{"class":94},[33,2160,2161],{"class":98},"().",[33,2163,2164],{"class":94},"addItem",[33,2166,2167],{"class":98},"(message.item)  ",[33,2169,2170],{"class":39},"\u002F\u002F no hook, no component\n",[33,2172,2173],{"class":35,"line":1426},[33,2174,1270],{"class":98},[33,2176,2177],{"class":35,"line":1446},[33,2178,275],{"class":98},[33,2180,2181],{"class":35,"line":1451},[33,2182,77],{"emptyLinePlaceholder":76},[33,2184,2185],{"class":35,"line":1508},[33,2186,2187],{"class":39},"\u002F\u002F This directly solves Context's \"can't read outside components\" limitation.\n",[401,2189,2191],{"id":2190},"zustand-anti-pattern-destructuring-without-a-selector","Zustand Anti-Pattern: Destructuring Without a Selector",[19,2193,2195],{"filename":2194,"language":22},"zustand_antipattern.js",[24,2196,2198],{"className":26,"code":2197,"language":22,"meta":28,"style":28},"\u002F\u002F ANTI-PATTERN: destructuring the whole store subscribes the component to\n\u002F\u002F EVERY field — quietly reintroducing Context's coarse-re-render problem\n\u002F\u002F inside a library designed to avoid it.\n\nfunction BadCartSummary() {\n  const { items, addItem, removeItem, clearCart } = useCartStore()\n  \u002F\u002F This component re-renders when ANY field in the store changes, even ones\n  \u002F\u002F it never uses. Exactly the problem you switched from Context to avoid.\n  return \u003Cdiv>{items.length} items\u003C\u002Fdiv>\n}\n\n\u002F\u002F CORRECT: use a selector for each piece of state you need:\nfunction GoodCartSummary() {\n  const items = useCartStore(state => state.items)\n  const addItem = useCartStore(state => state.addItem)\n  return \u003Cdiv>{items.length} items\u003C\u002Fdiv>\n}\n\n\u002F\u002F Or use useShallow (Zustand 4.4+) to subscribe to multiple fields with\n\u002F\u002F shallow equality comparison:\nimport { useShallow } from 'zustand\u002Freact\u002Fshallow'\n\nfunction CartSummary() {\n  const { items, addItem } = useCartStore(useShallow(state => ({\n    items: state.items,\n    addItem: state.addItem,\n  })))\n  \u002F\u002F Only re-renders when the shallow comparison of { items, addItem } changes\n  \u002F\u002F — addItem is stable (never changes), so this re-renders only when items changes.\n  return \u003Cdiv>{items.length} items\u003C\u002Fdiv>\n}\n",[30,2199,2200,2205,2210,2215,2219,2228,2259,2264,2269,2289,2293,2297,2302,2311,2331,2349,2367,2371,2375,2380,2385,2397,2401,2409,2440,2445,2450,2455,2460,2465,2483],{"__ignoreMap":28},[33,2201,2202],{"class":35,"line":36},[33,2203,2204],{"class":39},"\u002F\u002F ANTI-PATTERN: destructuring the whole store subscribes the component to\n",[33,2206,2207],{"class":35,"line":43},[33,2208,2209],{"class":39},"\u002F\u002F EVERY field — quietly reintroducing Context's coarse-re-render problem\n",[33,2211,2212],{"class":35,"line":49},[33,2213,2214],{"class":39},"\u002F\u002F inside a library designed to avoid it.\n",[33,2216,2217],{"class":35,"line":55},[33,2218,77],{"emptyLinePlaceholder":76},[33,2220,2221,2223,2226],{"class":35,"line":61},[33,2222,116],{"class":83},[33,2224,2225],{"class":94}," BadCartSummary",[33,2227,291],{"class":98},[33,2229,2230,2232,2234,2237,2239,2241,2243,2246,2248,2251,2253,2255,2257],{"class":35,"line":67},[33,2231,135],{"class":83},[33,2233,299],{"class":98},[33,2235,2236],{"class":87},"items",[33,2238,144],{"class":98},[33,2240,2164],{"class":87},[33,2242,144],{"class":98},[33,2244,2245],{"class":87},"removeItem",[33,2247,144],{"class":98},[33,2249,2250],{"class":87},"clearCart",[33,2252,308],{"class":98},[33,2254,153],{"class":83},[33,2256,1770],{"class":94},[33,2258,1121],{"class":98},[33,2260,2261],{"class":35,"line":73},[33,2262,2263],{"class":39},"  \u002F\u002F This component re-renders when ANY field in the store changes, even ones\n",[33,2265,2266],{"class":35,"line":80},[33,2267,2268],{"class":39},"  \u002F\u002F it never uses. Exactly the problem you switched from Context to avoid.\n",[33,2270,2271,2273,2275,2277,2280,2282,2285,2287],{"class":35,"line":108},[33,2272,251],{"class":83},[33,2274,254],{"class":98},[33,2276,999],{"class":344},[33,2278,2279],{"class":98},">{items.",[33,2281,2003],{"class":87},[33,2283,2284],{"class":98},"} items\u003C\u002F",[33,2286,999],{"class":344},[33,2288,269],{"class":98},[33,2290,2291],{"class":35,"line":113},[33,2292,275],{"class":98},[33,2294,2295],{"class":35,"line":132},[33,2296,77],{"emptyLinePlaceholder":76},[33,2298,2299],{"class":35,"line":165},[33,2300,2301],{"class":39},"\u002F\u002F CORRECT: use a selector for each piece of state you need:\n",[33,2303,2304,2306,2309],{"class":35,"line":194},[33,2305,116],{"class":83},[33,2307,2308],{"class":94}," GoodCartSummary",[33,2310,291],{"class":98},[33,2312,2313,2315,2318,2320,2322,2324,2326,2328],{"class":35,"line":218},[33,2314,135],{"class":83},[33,2316,2317],{"class":87}," items",[33,2319,91],{"class":83},[33,2321,1770],{"class":94},[33,2323,99],{"class":98},[33,2325,1820],{"class":125},[33,2327,367],{"class":83},[33,2329,2330],{"class":98}," state.items)\n",[33,2332,2333,2335,2337,2339,2341,2343,2345,2347],{"class":35,"line":223},[33,2334,135],{"class":83},[33,2336,2012],{"class":87},[33,2338,91],{"class":83},[33,2340,1770],{"class":94},[33,2342,99],{"class":98},[33,2344,1820],{"class":125},[33,2346,367],{"class":83},[33,2348,2025],{"class":98},[33,2350,2351,2353,2355,2357,2359,2361,2363,2365],{"class":35,"line":229},[33,2352,251],{"class":83},[33,2354,254],{"class":98},[33,2356,999],{"class":344},[33,2358,2279],{"class":98},[33,2360,2003],{"class":87},[33,2362,2284],{"class":98},[33,2364,999],{"class":344},[33,2366,269],{"class":98},[33,2368,2369],{"class":35,"line":235},[33,2370,275],{"class":98},[33,2372,2373],{"class":35,"line":248},[33,2374,77],{"emptyLinePlaceholder":76},[33,2376,2377],{"class":35,"line":272},[33,2378,2379],{"class":39},"\u002F\u002F Or use useShallow (Zustand 4.4+) to subscribe to multiple fields with\n",[33,2381,2382],{"class":35,"line":278},[33,2383,2384],{"class":39},"\u002F\u002F shallow equality comparison:\n",[33,2386,2387,2389,2392,2394],{"class":35,"line":283},[33,2388,1057],{"class":83},[33,2390,2391],{"class":98}," { useShallow } ",[33,2393,1063],{"class":83},[33,2395,2396],{"class":188}," 'zustand\u002Freact\u002Fshallow'\n",[33,2398,2399],{"class":35,"line":294},[33,2400,77],{"emptyLinePlaceholder":76},[33,2402,2403,2405,2407],{"class":35,"line":319},[33,2404,116],{"class":83},[33,2406,1968],{"class":94},[33,2408,291],{"class":98},[33,2410,2411,2413,2415,2417,2419,2421,2423,2425,2427,2429,2432,2434,2436,2438],{"class":35,"line":325},[33,2412,135],{"class":83},[33,2414,299],{"class":98},[33,2416,2236],{"class":87},[33,2418,144],{"class":98},[33,2420,2164],{"class":87},[33,2422,308],{"class":98},[33,2424,153],{"class":83},[33,2426,1770],{"class":94},[33,2428,99],{"class":98},[33,2430,2431],{"class":94},"useShallow",[33,2433,99],{"class":98},[33,2435,1820],{"class":125},[33,2437,367],{"class":83},[33,2439,1793],{"class":98},[33,2441,2442],{"class":35,"line":331},[33,2443,2444],{"class":98},"    items: state.items,\n",[33,2446,2447],{"class":35,"line":337},[33,2448,2449],{"class":98},"    addItem: state.addItem,\n",[33,2451,2452],{"class":35,"line":397},[33,2453,2454],{"class":98},"  })))\n",[33,2456,2457],{"class":35,"line":961},[33,2458,2459],{"class":39},"  \u002F\u002F Only re-renders when the shallow comparison of { items, addItem } changes\n",[33,2461,2462],{"class":35,"line":967},[33,2463,2464],{"class":39},"  \u002F\u002F — addItem is stable (never changes), so this re-renders only when items changes.\n",[33,2466,2467,2469,2471,2473,2475,2477,2479,2481],{"class":35,"line":977},[33,2468,251],{"class":83},[33,2470,254],{"class":98},[33,2472,999],{"class":344},[33,2474,2279],{"class":98},[33,2476,2003],{"class":87},[33,2478,2284],{"class":98},[33,2480,999],{"class":344},[33,2482,269],{"class":98},[33,2484,2485],{"class":35,"line":992},[33,2486,275],{"class":98},[401,2488,2490],{"id":2489},"zustand-with-middleware-persistence","Zustand with Middleware (Persistence)",[19,2492,2494],{"filename":2493,"language":22},"zustand_persistence.js",[24,2495,2497],{"className":26,"code":2496,"language":22,"meta":28,"style":28},"import { create } from 'zustand'\nimport { persist, createJSONStorage } from 'zustand\u002Fmiddleware'\n\nconst useAuthStore = create(\n  persist(\n    (set) => ({\n      user: null,\n      token: null,\n      login: (user, token) => set({ user, token }),\n      logout: () => set({ user: null, token: null }),\n    }),\n    {\n      name: 'auth-storage',  \u002F\u002F localStorage key\n      storage: createJSONStorage(() => localStorage),\n      partialize: (state) => ({ token: state.token }),  \u002F\u002F only persist token, not user\n    }\n  )\n)\n\n\u002F\u002F The store automatically hydrates from localStorage on app load and persists\n\u002F\u002F on every change. No manual useEffect + localStorage boilerplate.\n",[30,2498,2499,2509,2521,2525,2538,2545,2558,2568,2577,2600,2623,2628,2633,2647,2662,2681,2686,2690,2694,2698,2703],{"__ignoreMap":28},[33,2500,2501,2503,2505,2507],{"class":35,"line":36},[33,2502,1057],{"class":83},[33,2504,1754],{"class":98},[33,2506,1063],{"class":83},[33,2508,1759],{"class":188},[33,2510,2511,2513,2516,2518],{"class":35,"line":43},[33,2512,1057],{"class":83},[33,2514,2515],{"class":98}," { persist, createJSONStorage } ",[33,2517,1063],{"class":83},[33,2519,2520],{"class":188}," 'zustand\u002Fmiddleware'\n",[33,2522,2523],{"class":35,"line":49},[33,2524,77],{"emptyLinePlaceholder":76},[33,2526,2527,2529,2532,2534,2536],{"class":35,"line":55},[33,2528,84],{"class":83},[33,2530,2531],{"class":87}," useAuthStore",[33,2533,91],{"class":83},[33,2535,1775],{"class":94},[33,2537,1417],{"class":98},[33,2539,2540,2543],{"class":35,"line":61},[33,2541,2542],{"class":94},"  persist",[33,2544,1417],{"class":98},[33,2546,2547,2550,2552,2554,2556],{"class":35,"line":67},[33,2548,2549],{"class":98},"    (",[33,2551,1781],{"class":125},[33,2553,1155],{"class":98},[33,2555,356],{"class":83},[33,2557,1793],{"class":98},[33,2559,2560,2563,2565],{"class":35,"line":73},[33,2561,2562],{"class":98},"      user: ",[33,2564,102],{"class":87},[33,2566,2567],{"class":98},",\n",[33,2569,2570,2573,2575],{"class":35,"line":80},[33,2571,2572],{"class":98},"      token: ",[33,2574,102],{"class":87},[33,2576,2567],{"class":98},[33,2578,2579,2582,2584,2586,2588,2591,2593,2595,2597],{"class":35,"line":108},[33,2580,2581],{"class":94},"      login",[33,2583,1149],{"class":98},[33,2585,141],{"class":125},[33,2587,144],{"class":98},[33,2589,2590],{"class":125},"token",[33,2592,1155],{"class":98},[33,2594,356],{"class":83},[33,2596,1815],{"class":94},[33,2598,2599],{"class":98},"({ user, token }),\n",[33,2601,2602,2605,2607,2609,2611,2613,2615,2618,2620],{"class":35,"line":113},[33,2603,2604],{"class":94},"      logout",[33,2606,1136],{"class":98},[33,2608,356],{"class":83},[33,2610,1815],{"class":94},[33,2612,1292],{"class":98},[33,2614,102],{"class":87},[33,2616,2617],{"class":98},", token: ",[33,2619,102],{"class":87},[33,2621,2622],{"class":98}," }),\n",[33,2624,2625],{"class":35,"line":132},[33,2626,2627],{"class":98},"    }),\n",[33,2629,2630],{"class":35,"line":165},[33,2631,2632],{"class":98},"    {\n",[33,2634,2635,2638,2641,2644],{"class":35,"line":194},[33,2636,2637],{"class":98},"      name: ",[33,2639,2640],{"class":188},"'auth-storage'",[33,2642,2643],{"class":98},",  ",[33,2645,2646],{"class":39},"\u002F\u002F localStorage key\n",[33,2648,2649,2652,2655,2657,2659],{"class":35,"line":218},[33,2650,2651],{"class":98},"      storage: ",[33,2653,2654],{"class":94},"createJSONStorage",[33,2656,765],{"class":98},[33,2658,356],{"class":83},[33,2660,2661],{"class":98}," localStorage),\n",[33,2663,2664,2667,2669,2671,2673,2675,2678],{"class":35,"line":223},[33,2665,2666],{"class":94},"      partialize",[33,2668,1149],{"class":98},[33,2670,1820],{"class":125},[33,2672,1155],{"class":98},[33,2674,356],{"class":83},[33,2676,2677],{"class":98}," ({ token: state.token }),  ",[33,2679,2680],{"class":39},"\u002F\u002F only persist token, not user\n",[33,2682,2683],{"class":35,"line":229},[33,2684,2685],{"class":98},"    }\n",[33,2687,2688],{"class":35,"line":235},[33,2689,608],{"class":98},[33,2691,2692],{"class":35,"line":248},[33,2693,105],{"class":98},[33,2695,2696],{"class":35,"line":272},[33,2697,77],{"emptyLinePlaceholder":76},[33,2699,2700],{"class":35,"line":278},[33,2701,2702],{"class":39},"\u002F\u002F The store automatically hydrates from localStorage on app load and persists\n",[33,2704,2705],{"class":35,"line":283},[33,2706,2707],{"class":39},"\u002F\u002F on every change. No manual useEffect + localStorage boilerplate.\n",[14,2709,2711],{"id":2710},"redux-toolkit-centralized-predictable-state","Redux Toolkit: Centralized, Predictable State",[19,2713,2715],{"filename":2714,"language":22},"redux_toolkit.js",[24,2716,2718],{"className":26,"code":2717,"language":22,"meta":28,"style":28},"\u002F\u002F Redux's core idea: one global store, state changes only via dispatched actions\n\u002F\u002F processed by pure reducer functions. Redux Toolkit (RTK) is the modern,\n\u002F\u002F officially-recommended way — eliminates nearly all legacy Redux boilerplate.\n\nimport { createSlice, configureStore, useSelector, useDispatch } from '@reduxjs\u002Ftoolkit'\n\nconst cartSlice = createSlice({\n  name: 'cart',\n  initialState: { items: [] },\n  reducers: {\n    addItem(state, action) {\n      \u002F\u002F RTK uses Immer internally — this LOOKS like direct mutation but produces\n      \u002F\u002F an immutable update behind the scenes. NEVER rely on this outside RTK's\n      \u002F\u002F reducers — in a plain useState updater, this would be a real broken mutation.\n      state.items.push(action.payload)\n    },\n    removeItem(state, action) {\n      state.items = state.items.filter(item => item.id !== action.payload)\n    },\n    clearCart(state) {\n      state.items = []\n    },\n  },\n})\n\nexport const { addItem, removeItem, clearCart } = cartSlice.actions\n\nconst store = configureStore({\n  reducer: { cart: cartSlice.reducer },\n})\n\n\u002F\u002F Provider wraps the app (unlike Zustand, Redux requires a Provider):\n\u002F\u002F \u003CProvider store={store}>\u003CApp \u002F>\u003C\u002FProvider>\n\nfunction CartSummary() {\n  \u002F\u002F useSelector subscribes ONLY to the slice of state this component reads —\n  \u002F\u002F a change to unrelated state elsewhere in the store does NOT re-render this.\n  const itemCount = useSelector(state => state.cart.items.length)\n  const dispatch = useDispatch()\n\n  return (\n    \u003Cdiv>\n      \u003Cspan>{itemCount} items\u003C\u002Fspan>\n      \u003Cbutton onClick={() => dispatch(addItem({ id: 3, name: 'Widget' }))}>Add Widget\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n",[30,2719,2720,2725,2730,2735,2739,2751,2755,2770,2780,2785,2790,2806,2811,2816,2821,2832,2836,2851,2875,2879,2890,2899,2903,2908,2913,2917,2944,2948,2961,2966,2970,2974,2979,2984,2988,2996,3001,3006,3030,3044,3048,3055,3064,3077,3113,3122,3127],{"__ignoreMap":28},[33,2721,2722],{"class":35,"line":36},[33,2723,2724],{"class":39},"\u002F\u002F Redux's core idea: one global store, state changes only via dispatched actions\n",[33,2726,2727],{"class":35,"line":43},[33,2728,2729],{"class":39},"\u002F\u002F processed by pure reducer functions. Redux Toolkit (RTK) is the modern,\n",[33,2731,2732],{"class":35,"line":49},[33,2733,2734],{"class":39},"\u002F\u002F officially-recommended way — eliminates nearly all legacy Redux boilerplate.\n",[33,2736,2737],{"class":35,"line":55},[33,2738,77],{"emptyLinePlaceholder":76},[33,2740,2741,2743,2746,2748],{"class":35,"line":61},[33,2742,1057],{"class":83},[33,2744,2745],{"class":98}," { createSlice, configureStore, useSelector, useDispatch } ",[33,2747,1063],{"class":83},[33,2749,2750],{"class":188}," '@reduxjs\u002Ftoolkit'\n",[33,2752,2753],{"class":35,"line":67},[33,2754,77],{"emptyLinePlaceholder":76},[33,2756,2757,2759,2762,2764,2767],{"class":35,"line":73},[33,2758,84],{"class":83},[33,2760,2761],{"class":87}," cartSlice",[33,2763,91],{"class":83},[33,2765,2766],{"class":94}," createSlice",[33,2768,2769],{"class":98},"({\n",[33,2771,2772,2775,2778],{"class":35,"line":80},[33,2773,2774],{"class":98},"  name: ",[33,2776,2777],{"class":188},"'cart'",[33,2779,2567],{"class":98},[33,2781,2782],{"class":35,"line":108},[33,2783,2784],{"class":98},"  initialState: { items: [] },\n",[33,2786,2787],{"class":35,"line":113},[33,2788,2789],{"class":98},"  reducers: {\n",[33,2791,2792,2795,2797,2799,2801,2804],{"class":35,"line":132},[33,2793,2794],{"class":94},"    addItem",[33,2796,99],{"class":98},[33,2798,1820],{"class":125},[33,2800,144],{"class":98},[33,2802,2803],{"class":125},"action",[33,2805,1090],{"class":98},[33,2807,2808],{"class":35,"line":165},[33,2809,2810],{"class":39},"      \u002F\u002F RTK uses Immer internally — this LOOKS like direct mutation but produces\n",[33,2812,2813],{"class":35,"line":194},[33,2814,2815],{"class":39},"      \u002F\u002F an immutable update behind the scenes. NEVER rely on this outside RTK's\n",[33,2817,2818],{"class":35,"line":218},[33,2819,2820],{"class":39},"      \u002F\u002F reducers — in a plain useState updater, this would be a real broken mutation.\n",[33,2822,2823,2826,2829],{"class":35,"line":223},[33,2824,2825],{"class":98},"      state.items.",[33,2827,2828],{"class":94},"push",[33,2830,2831],{"class":98},"(action.payload)\n",[33,2833,2834],{"class":35,"line":229},[33,2835,1217],{"class":98},[33,2837,2838,2841,2843,2845,2847,2849],{"class":35,"line":235},[33,2839,2840],{"class":94},"    removeItem",[33,2842,99],{"class":98},[33,2844,1820],{"class":125},[33,2846,144],{"class":98},[33,2848,2803],{"class":125},[33,2850,1090],{"class":98},[33,2852,2853,2856,2858,2860,2862,2864,2866,2868,2870,2872],{"class":35,"line":248},[33,2854,2855],{"class":98},"      state.items ",[33,2857,153],{"class":83},[33,2859,2000],{"class":98},[33,2861,1858],{"class":94},[33,2863,99],{"class":98},[33,2865,1808],{"class":125},[33,2867,367],{"class":83},[33,2869,1867],{"class":98},[33,2871,1870],{"class":83},[33,2873,2874],{"class":98}," action.payload)\n",[33,2876,2877],{"class":35,"line":272},[33,2878,1217],{"class":98},[33,2880,2881,2884,2886,2888],{"class":35,"line":278},[33,2882,2883],{"class":94},"    clearCart",[33,2885,99],{"class":98},[33,2887,1820],{"class":125},[33,2889,1090],{"class":98},[33,2891,2892,2894,2896],{"class":35,"line":283},[33,2893,2855],{"class":98},[33,2895,153],{"class":83},[33,2897,2898],{"class":98}," []\n",[33,2900,2901],{"class":35,"line":294},[33,2902,1217],{"class":98},[33,2904,2905],{"class":35,"line":319},[33,2906,2907],{"class":98},"  },\n",[33,2909,2910],{"class":35,"line":325},[33,2911,2912],{"class":98},"})\n",[33,2914,2915],{"class":35,"line":331},[33,2916,77],{"emptyLinePlaceholder":76},[33,2918,2919,2922,2925,2927,2929,2931,2933,2935,2937,2939,2941],{"class":35,"line":337},[33,2920,2921],{"class":83},"export",[33,2923,2924],{"class":83}," const",[33,2926,299],{"class":98},[33,2928,2164],{"class":87},[33,2930,144],{"class":98},[33,2932,2245],{"class":87},[33,2934,144],{"class":98},[33,2936,2250],{"class":87},[33,2938,308],{"class":98},[33,2940,153],{"class":83},[33,2942,2943],{"class":98}," cartSlice.actions\n",[33,2945,2946],{"class":35,"line":397},[33,2947,77],{"emptyLinePlaceholder":76},[33,2949,2950,2952,2954,2956,2959],{"class":35,"line":961},[33,2951,84],{"class":83},[33,2953,1285],{"class":87},[33,2955,91],{"class":83},[33,2957,2958],{"class":94}," configureStore",[33,2960,2769],{"class":98},[33,2962,2963],{"class":35,"line":967},[33,2964,2965],{"class":98},"  reducer: { cart: cartSlice.reducer },\n",[33,2967,2968],{"class":35,"line":977},[33,2969,2912],{"class":98},[33,2971,2972],{"class":35,"line":992},[33,2973,77],{"emptyLinePlaceholder":76},[33,2975,2976],{"class":35,"line":1024},[33,2977,2978],{"class":39},"\u002F\u002F Provider wraps the app (unlike Zustand, Redux requires a Provider):\n",[33,2980,2981],{"class":35,"line":1393},[33,2982,2983],{"class":39},"\u002F\u002F \u003CProvider store={store}>\u003CApp \u002F>\u003C\u002FProvider>\n",[33,2985,2986],{"class":35,"line":1399},[33,2987,77],{"emptyLinePlaceholder":76},[33,2989,2990,2992,2994],{"class":35,"line":1405},[33,2991,116],{"class":83},[33,2993,1968],{"class":94},[33,2995,291],{"class":98},[33,2997,2998],{"class":35,"line":1420},[33,2999,3000],{"class":39},"  \u002F\u002F useSelector subscribes ONLY to the slice of state this component reads —\n",[33,3002,3003],{"class":35,"line":1426},[33,3004,3005],{"class":39},"  \u002F\u002F a change to unrelated state elsewhere in the store does NOT re-render this.\n",[33,3007,3008,3010,3012,3014,3017,3019,3021,3023,3026,3028],{"class":35,"line":1446},[33,3009,135],{"class":83},[33,3011,1987],{"class":87},[33,3013,91],{"class":83},[33,3015,3016],{"class":94}," useSelector",[33,3018,99],{"class":98},[33,3020,1820],{"class":125},[33,3022,367],{"class":83},[33,3024,3025],{"class":98}," state.cart.items.",[33,3027,2003],{"class":87},[33,3029,105],{"class":98},[33,3031,3032,3034,3037,3039,3042],{"class":35,"line":1451},[33,3033,135],{"class":83},[33,3035,3036],{"class":87}," dispatch",[33,3038,91],{"class":83},[33,3040,3041],{"class":94}," useDispatch",[33,3043,1121],{"class":98},[33,3045,3046],{"class":35,"line":1508},[33,3047,77],{"emptyLinePlaceholder":76},[33,3049,3051,3053],{"class":35,"line":3050},41,[33,3052,251],{"class":83},[33,3054,505],{"class":98},[33,3056,3058,3060,3062],{"class":35,"line":3057},42,[33,3059,510],{"class":98},[33,3061,999],{"class":344},[33,3063,269],{"class":98},[33,3065,3067,3069,3071,3073,3075],{"class":35,"line":3066},43,[33,3068,535],{"class":98},[33,3070,33],{"class":344},[33,3072,2052],{"class":98},[33,3074,33],{"class":344},[33,3076,269],{"class":98},[33,3078,3080,3082,3084,3086,3088,3090,3092,3094,3096,3098,3100,3102,3104,3106,3109,3111],{"class":35,"line":3079},44,[33,3081,535],{"class":98},[33,3083,345],{"class":344},[33,3085,348],{"class":94},[33,3087,153],{"class":83},[33,3089,353],{"class":98},[33,3091,356],{"class":83},[33,3093,3036],{"class":94},[33,3095,99],{"class":98},[33,3097,2164],{"class":94},[33,3099,2075],{"class":98},[33,3101,2078],{"class":87},[33,3103,2081],{"class":98},[33,3105,2084],{"class":188},[33,3107,3108],{"class":98}," }))}>Add Widget\u003C\u002F",[33,3110,345],{"class":344},[33,3112,269],{"class":98},[33,3114,3116,3118,3120],{"class":35,"line":3115},45,[33,3117,599],{"class":98},[33,3119,999],{"class":344},[33,3121,269],{"class":98},[33,3123,3125],{"class":35,"line":3124},46,[33,3126,608],{"class":98},[33,3128,3130],{"class":35,"line":3129},47,[33,3131,275],{"class":98},[401,3133,3135],{"id":3134},"redux-toolkit-async-thunks","Redux Toolkit: Async Thunks",[19,3137,3139],{"filename":3138,"language":22},"rtk_thunks.js",[24,3140,3142],{"className":26,"code":3141,"language":22,"meta":28,"style":28},"import { createAsyncThunk } from '@reduxjs\u002Ftoolkit'\n\n\u002F\u002F createAsyncThunk handles the pending\u002Ffulfilled\u002Frejected lifecycle automatically:\nexport const fetchProducts = createAsyncThunk('products\u002Ffetch', async () => {\n  const res = await fetch('\u002Fapi\u002Fproducts')\n  if (!res.ok) throw new Error(`HTTP ${res.status}`)\n  return res.json()\n})\n\nconst productsSlice = createSlice({\n  name: 'products',\n  initialState: { items: [], status: 'idle', error: null },\n  reducers: {},\n  extraReducers: (builder) => {\n    builder\n      .addCase(fetchProducts.pending, (state) => {\n        state.status = 'loading'\n      })\n      .addCase(fetchProducts.fulfilled, (state, action) => {\n        state.status = 'success'\n        state.items = action.payload\n      })\n      .addCase(fetchProducts.rejected, (state, action) => {\n        state.status = 'error'\n        state.error = action.error.message\n      })\n  },\n})\n\n\u002F\u002F Dispatch the thunk from a component:\nfunction ProductList() {\n  const { items, status, error } = useSelector(state => state.products)\n  const dispatch = useDispatch()\n\n  useEffect(() => {\n    if (status === 'idle') dispatch(fetchProducts())\n  }, [status, dispatch])\n\n  if (status === 'loading') return \u003CSpinner \u002F>\n  if (status === 'error') return \u003CErrorMessage error={error} \u002F>\n  return \u003Cul>{items.map(p => \u003Cli key={p.id}>{p.name}\u003C\u002Fli>)}\u003C\u002Ful>\n}\n",[30,3143,3144,3155,3159,3164,3194,3216,3255,3267,3271,3275,3288,3297,3313,3318,3334,3339,3358,3368,3373,3394,3403,3413,3417,3438,3447,3457,3461,3465,3469,3473,3478,3487,3519,3531,3535,3546,3571,3576,3580,3604,3632,3675],{"__ignoreMap":28},[33,3145,3146,3148,3151,3153],{"class":35,"line":36},[33,3147,1057],{"class":83},[33,3149,3150],{"class":98}," { createAsyncThunk } ",[33,3152,1063],{"class":83},[33,3154,2750],{"class":188},[33,3156,3157],{"class":35,"line":43},[33,3158,77],{"emptyLinePlaceholder":76},[33,3160,3161],{"class":35,"line":49},[33,3162,3163],{"class":39},"\u002F\u002F createAsyncThunk handles the pending\u002Ffulfilled\u002Frejected lifecycle automatically:\n",[33,3165,3166,3168,3170,3173,3175,3178,3180,3183,3185,3188,3190,3192],{"class":35,"line":55},[33,3167,2921],{"class":83},[33,3169,2924],{"class":83},[33,3171,3172],{"class":87}," fetchProducts",[33,3174,91],{"class":83},[33,3176,3177],{"class":94}," createAsyncThunk",[33,3179,99],{"class":98},[33,3181,3182],{"class":188},"'products\u002Ffetch'",[33,3184,144],{"class":98},[33,3186,3187],{"class":83},"async",[33,3189,1251],{"class":98},[33,3191,356],{"class":83},[33,3193,1128],{"class":98},[33,3195,3196,3198,3201,3203,3206,3209,3211,3214],{"class":35,"line":61},[33,3197,135],{"class":83},[33,3199,3200],{"class":87}," res",[33,3202,91],{"class":83},[33,3204,3205],{"class":83}," await",[33,3207,3208],{"class":94}," fetch",[33,3210,99],{"class":98},[33,3212,3213],{"class":188},"'\u002Fapi\u002Fproducts'",[33,3215,105],{"class":98},[33,3217,3218,3220,3223,3226,3229,3232,3234,3237,3239,3242,3245,3248,3251,3253],{"class":35,"line":67},[33,3219,2141],{"class":83},[33,3221,3222],{"class":98}," (",[33,3224,3225],{"class":83},"!",[33,3227,3228],{"class":98},"res.ok) ",[33,3230,3231],{"class":83},"throw",[33,3233,1115],{"class":83},[33,3235,3236],{"class":94}," Error",[33,3238,99],{"class":98},[33,3240,3241],{"class":188},"`HTTP ${",[33,3243,3244],{"class":98},"res",[33,3246,3247],{"class":188},".",[33,3249,3250],{"class":98},"status",[33,3252,1014],{"class":188},[33,3254,105],{"class":98},[33,3256,3257,3259,3262,3265],{"class":35,"line":73},[33,3258,251],{"class":83},[33,3260,3261],{"class":98}," res.",[33,3263,3264],{"class":94},"json",[33,3266,1121],{"class":98},[33,3268,3269],{"class":35,"line":80},[33,3270,2912],{"class":98},[33,3272,3273],{"class":35,"line":108},[33,3274,77],{"emptyLinePlaceholder":76},[33,3276,3277,3279,3282,3284,3286],{"class":35,"line":113},[33,3278,84],{"class":83},[33,3280,3281],{"class":87}," productsSlice",[33,3283,91],{"class":83},[33,3285,2766],{"class":94},[33,3287,2769],{"class":98},[33,3289,3290,3292,3295],{"class":35,"line":132},[33,3291,2774],{"class":98},[33,3293,3294],{"class":188},"'products'",[33,3296,2567],{"class":98},[33,3298,3299,3302,3305,3308,3310],{"class":35,"line":165},[33,3300,3301],{"class":98},"  initialState: { items: [], status: ",[33,3303,3304],{"class":188},"'idle'",[33,3306,3307],{"class":98},", error: ",[33,3309,102],{"class":87},[33,3311,3312],{"class":98}," },\n",[33,3314,3315],{"class":35,"line":194},[33,3316,3317],{"class":98},"  reducers: {},\n",[33,3319,3320,3323,3325,3328,3330,3332],{"class":35,"line":218},[33,3321,3322],{"class":94},"  extraReducers",[33,3324,1149],{"class":98},[33,3326,3327],{"class":125},"builder",[33,3329,1155],{"class":98},[33,3331,356],{"class":83},[33,3333,1128],{"class":98},[33,3335,3336],{"class":35,"line":223},[33,3337,3338],{"class":98},"    builder\n",[33,3340,3341,3344,3347,3350,3352,3354,3356],{"class":35,"line":229},[33,3342,3343],{"class":98},"      .",[33,3345,3346],{"class":94},"addCase",[33,3348,3349],{"class":98},"(fetchProducts.pending, (",[33,3351,1820],{"class":125},[33,3353,1155],{"class":98},[33,3355,356],{"class":83},[33,3357,1128],{"class":98},[33,3359,3360,3363,3365],{"class":35,"line":235},[33,3361,3362],{"class":98},"        state.status ",[33,3364,153],{"class":83},[33,3366,3367],{"class":188}," 'loading'\n",[33,3369,3370],{"class":35,"line":248},[33,3371,3372],{"class":98},"      })\n",[33,3374,3375,3377,3379,3382,3384,3386,3388,3390,3392],{"class":35,"line":272},[33,3376,3343],{"class":98},[33,3378,3346],{"class":94},[33,3380,3381],{"class":98},"(fetchProducts.fulfilled, (",[33,3383,1820],{"class":125},[33,3385,144],{"class":98},[33,3387,2803],{"class":125},[33,3389,1155],{"class":98},[33,3391,356],{"class":83},[33,3393,1128],{"class":98},[33,3395,3396,3398,3400],{"class":35,"line":278},[33,3397,3362],{"class":98},[33,3399,153],{"class":83},[33,3401,3402],{"class":188}," 'success'\n",[33,3404,3405,3408,3410],{"class":35,"line":283},[33,3406,3407],{"class":98},"        state.items ",[33,3409,153],{"class":83},[33,3411,3412],{"class":98}," action.payload\n",[33,3414,3415],{"class":35,"line":294},[33,3416,3372],{"class":98},[33,3418,3419,3421,3423,3426,3428,3430,3432,3434,3436],{"class":35,"line":319},[33,3420,3343],{"class":98},[33,3422,3346],{"class":94},[33,3424,3425],{"class":98},"(fetchProducts.rejected, (",[33,3427,1820],{"class":125},[33,3429,144],{"class":98},[33,3431,2803],{"class":125},[33,3433,1155],{"class":98},[33,3435,356],{"class":83},[33,3437,1128],{"class":98},[33,3439,3440,3442,3444],{"class":35,"line":325},[33,3441,3362],{"class":98},[33,3443,153],{"class":83},[33,3445,3446],{"class":188}," 'error'\n",[33,3448,3449,3452,3454],{"class":35,"line":331},[33,3450,3451],{"class":98},"        state.error ",[33,3453,153],{"class":83},[33,3455,3456],{"class":98}," action.error.message\n",[33,3458,3459],{"class":35,"line":337},[33,3460,3372],{"class":98},[33,3462,3463],{"class":35,"line":397},[33,3464,2907],{"class":98},[33,3466,3467],{"class":35,"line":961},[33,3468,2912],{"class":98},[33,3470,3471],{"class":35,"line":967},[33,3472,77],{"emptyLinePlaceholder":76},[33,3474,3475],{"class":35,"line":977},[33,3476,3477],{"class":39},"\u002F\u002F Dispatch the thunk from a component:\n",[33,3479,3480,3482,3485],{"class":35,"line":992},[33,3481,116],{"class":83},[33,3483,3484],{"class":94}," ProductList",[33,3486,291],{"class":98},[33,3488,3489,3491,3493,3495,3497,3499,3501,3504,3506,3508,3510,3512,3514,3516],{"class":35,"line":1024},[33,3490,135],{"class":83},[33,3492,299],{"class":98},[33,3494,2236],{"class":87},[33,3496,144],{"class":98},[33,3498,3250],{"class":87},[33,3500,144],{"class":98},[33,3502,3503],{"class":87},"error",[33,3505,308],{"class":98},[33,3507,153],{"class":83},[33,3509,3016],{"class":94},[33,3511,99],{"class":98},[33,3513,1820],{"class":125},[33,3515,367],{"class":83},[33,3517,3518],{"class":98}," state.products)\n",[33,3520,3521,3523,3525,3527,3529],{"class":35,"line":1393},[33,3522,135],{"class":83},[33,3524,3036],{"class":87},[33,3526,91],{"class":83},[33,3528,3041],{"class":94},[33,3530,1121],{"class":98},[33,3532,3533],{"class":35,"line":1399},[33,3534,77],{"emptyLinePlaceholder":76},[33,3536,3537,3540,3542,3544],{"class":35,"line":1405},[33,3538,3539],{"class":94},"  useEffect",[33,3541,765],{"class":98},[33,3543,356],{"class":83},[33,3545,1128],{"class":98},[33,3547,3548,3551,3554,3556,3559,3561,3564,3566,3569],{"class":35,"line":1420},[33,3549,3550],{"class":83},"    if",[33,3552,3553],{"class":98}," (status ",[33,3555,373],{"class":83},[33,3557,3558],{"class":188}," 'idle'",[33,3560,1155],{"class":98},[33,3562,3563],{"class":94},"dispatch",[33,3565,99],{"class":98},[33,3567,3568],{"class":94},"fetchProducts",[33,3570,1212],{"class":98},[33,3572,3573],{"class":35,"line":1426},[33,3574,3575],{"class":98},"  }, [status, dispatch])\n",[33,3577,3578],{"class":35,"line":1446},[33,3579,77],{"emptyLinePlaceholder":76},[33,3581,3582,3584,3586,3588,3591,3593,3596,3598,3601],{"class":35,"line":1451},[33,3583,2141],{"class":83},[33,3585,3553],{"class":98},[33,3587,373],{"class":83},[33,3589,3590],{"class":188}," 'loading'",[33,3592,1155],{"class":98},[33,3594,3595],{"class":83},"return",[33,3597,254],{"class":98},[33,3599,3600],{"class":87},"Spinner",[33,3602,3603],{"class":98}," \u002F>\n",[33,3605,3606,3608,3610,3612,3615,3617,3619,3621,3624,3627,3629],{"class":35,"line":1508},[33,3607,2141],{"class":83},[33,3609,3553],{"class":98},[33,3611,373],{"class":83},[33,3613,3614],{"class":188}," 'error'",[33,3616,1155],{"class":98},[33,3618,3595],{"class":83},[33,3620,254],{"class":98},[33,3622,3623],{"class":87},"ErrorMessage",[33,3625,3626],{"class":94}," error",[33,3628,153],{"class":83},[33,3630,3631],{"class":98},"{error} \u002F>\n",[33,3633,3634,3636,3638,3641,3643,3646,3648,3651,3653,3655,3658,3661,3663,3666,3668,3671,3673],{"class":35,"line":3050},[33,3635,251],{"class":83},[33,3637,254],{"class":98},[33,3639,3640],{"class":344},"ul",[33,3642,2279],{"class":98},[33,3644,3645],{"class":94},"map",[33,3647,99],{"class":98},[33,3649,3650],{"class":125},"p",[33,3652,367],{"class":83},[33,3654,254],{"class":98},[33,3656,3657],{"class":344},"li",[33,3659,3660],{"class":94}," key",[33,3662,153],{"class":83},[33,3664,3665],{"class":98},"{p.id}>{p.name}\u003C\u002F",[33,3667,3657],{"class":344},[33,3669,3670],{"class":98},">)}\u003C\u002F",[33,3672,3640],{"class":344},[33,3674,269],{"class":98},[33,3676,3677],{"class":35,"line":3057},[33,3678,275],{"class":98},[401,3680,3682],{"id":3681},"redux-toolkit-query-rtk-query","Redux Toolkit Query (RTK Query)",[19,3684,3686],{"filename":3685,"language":22},"rtk_query.js",[24,3687,3689],{"className":26,"code":3688,"language":22,"meta":28,"style":28},"\u002F\u002F RTK Query is built into Redux Toolkit and provides the same caching\u002Frefetching\n\u002F\u002F benefits as React Query, integrated directly into the Redux store.\n\nimport { createApi, fetchBaseQuery } from '@reduxjs\u002Ftoolkit\u002Fquery\u002Freact'\n\nconst productsApi = createApi({\n  reducerPath: 'productsApi',\n  baseQuery: fetchBaseQuery({ baseUrl: '\u002Fapi' }),\n  endpoints: (builder) => ({\n    getProducts: builder.query({ query: () => 'products' }),\n    getProduct: builder.query({ query: (id) => `products\u002F${id}` }),\n    addProduct: builder.mutation({\n      query: (body) => ({ url: 'products', method: 'POST', body }),\n    }),\n  }),\n})\n\nexport const { useGetProductsQuery, useGetProductQuery, useAddProductMutation } = productsApi\n\nconst store = configureStore({\n  reducer: {\n    [productsApi.reducerPath]: productsApi.reducer,\n    \u002F\u002F ...other slices\n  },\n  middleware: (getDefault) => getDefault().concat(productsApi.middleware),\n})\n\nfunction ProductList() {\n  const { data, isLoading, error } = useGetProductsQuery()\n  if (isLoading) return \u003CSpinner \u002F>\n  if (error) return \u003CErrorMessage error={error} \u002F>\n  return \u003Cul>{data.map(p => \u003Cli key={p.id}>{p.name}\u003C\u002Fli>)}\u003C\u002Ful>\n}\n",[30,3690,3691,3696,3701,3705,3717,3721,3735,3745,3761,3776,3797,3825,3835,3863,3867,3872,3876,3880,3908,3912,3924,3929,3934,3939,3943,3968,3972,3976,3984,4011,4026,4045,4082],{"__ignoreMap":28},[33,3692,3693],{"class":35,"line":36},[33,3694,3695],{"class":39},"\u002F\u002F RTK Query is built into Redux Toolkit and provides the same caching\u002Frefetching\n",[33,3697,3698],{"class":35,"line":43},[33,3699,3700],{"class":39},"\u002F\u002F benefits as React Query, integrated directly into the Redux store.\n",[33,3702,3703],{"class":35,"line":49},[33,3704,77],{"emptyLinePlaceholder":76},[33,3706,3707,3709,3712,3714],{"class":35,"line":55},[33,3708,1057],{"class":83},[33,3710,3711],{"class":98}," { createApi, fetchBaseQuery } ",[33,3713,1063],{"class":83},[33,3715,3716],{"class":188}," '@reduxjs\u002Ftoolkit\u002Fquery\u002Freact'\n",[33,3718,3719],{"class":35,"line":61},[33,3720,77],{"emptyLinePlaceholder":76},[33,3722,3723,3725,3728,3730,3733],{"class":35,"line":67},[33,3724,84],{"class":83},[33,3726,3727],{"class":87}," productsApi",[33,3729,91],{"class":83},[33,3731,3732],{"class":94}," createApi",[33,3734,2769],{"class":98},[33,3736,3737,3740,3743],{"class":35,"line":73},[33,3738,3739],{"class":98},"  reducerPath: ",[33,3741,3742],{"class":188},"'productsApi'",[33,3744,2567],{"class":98},[33,3746,3747,3750,3753,3756,3759],{"class":35,"line":80},[33,3748,3749],{"class":98},"  baseQuery: ",[33,3751,3752],{"class":94},"fetchBaseQuery",[33,3754,3755],{"class":98},"({ baseUrl: ",[33,3757,3758],{"class":188},"'\u002Fapi'",[33,3760,2622],{"class":98},[33,3762,3763,3766,3768,3770,3772,3774],{"class":35,"line":108},[33,3764,3765],{"class":94},"  endpoints",[33,3767,1149],{"class":98},[33,3769,3327],{"class":125},[33,3771,1155],{"class":98},[33,3773,356],{"class":83},[33,3775,1793],{"class":98},[33,3777,3778,3781,3784,3786,3788,3790,3792,3795],{"class":35,"line":113},[33,3779,3780],{"class":98},"    getProducts: builder.",[33,3782,3783],{"class":94},"query",[33,3785,122],{"class":98},[33,3787,3783],{"class":94},[33,3789,1136],{"class":98},[33,3791,356],{"class":83},[33,3793,3794],{"class":188}," 'products'",[33,3796,2622],{"class":98},[33,3798,3799,3802,3804,3806,3808,3810,3812,3814,3816,3819,3821,3823],{"class":35,"line":132},[33,3800,3801],{"class":98},"    getProduct: builder.",[33,3803,3783],{"class":94},[33,3805,122],{"class":98},[33,3807,3783],{"class":94},[33,3809,1149],{"class":98},[33,3811,1840],{"class":125},[33,3813,1155],{"class":98},[33,3815,356],{"class":83},[33,3817,3818],{"class":188}," `products\u002F${",[33,3820,1840],{"class":98},[33,3822,1014],{"class":188},[33,3824,2622],{"class":98},[33,3826,3827,3830,3833],{"class":35,"line":165},[33,3828,3829],{"class":98},"    addProduct: builder.",[33,3831,3832],{"class":94},"mutation",[33,3834,2769],{"class":98},[33,3836,3837,3840,3842,3845,3847,3849,3852,3854,3857,3860],{"class":35,"line":194},[33,3838,3839],{"class":94},"      query",[33,3841,1149],{"class":98},[33,3843,3844],{"class":125},"body",[33,3846,1155],{"class":98},[33,3848,356],{"class":83},[33,3850,3851],{"class":98}," ({ url: ",[33,3853,3294],{"class":188},[33,3855,3856],{"class":98},", method: ",[33,3858,3859],{"class":188},"'POST'",[33,3861,3862],{"class":98},", body }),\n",[33,3864,3865],{"class":35,"line":218},[33,3866,2627],{"class":98},[33,3868,3869],{"class":35,"line":223},[33,3870,3871],{"class":98},"  }),\n",[33,3873,3874],{"class":35,"line":229},[33,3875,2912],{"class":98},[33,3877,3878],{"class":35,"line":235},[33,3879,77],{"emptyLinePlaceholder":76},[33,3881,3882,3884,3886,3888,3891,3893,3896,3898,3901,3903,3905],{"class":35,"line":248},[33,3883,2921],{"class":83},[33,3885,2924],{"class":83},[33,3887,299],{"class":98},[33,3889,3890],{"class":87},"useGetProductsQuery",[33,3892,144],{"class":98},[33,3894,3895],{"class":87},"useGetProductQuery",[33,3897,144],{"class":98},[33,3899,3900],{"class":87},"useAddProductMutation",[33,3902,308],{"class":98},[33,3904,153],{"class":83},[33,3906,3907],{"class":98}," productsApi\n",[33,3909,3910],{"class":35,"line":272},[33,3911,77],{"emptyLinePlaceholder":76},[33,3913,3914,3916,3918,3920,3922],{"class":35,"line":278},[33,3915,84],{"class":83},[33,3917,1285],{"class":87},[33,3919,91],{"class":83},[33,3921,2958],{"class":94},[33,3923,2769],{"class":98},[33,3925,3926],{"class":35,"line":283},[33,3927,3928],{"class":98},"  reducer: {\n",[33,3930,3931],{"class":35,"line":294},[33,3932,3933],{"class":98},"    [productsApi.reducerPath]: productsApi.reducer,\n",[33,3935,3936],{"class":35,"line":319},[33,3937,3938],{"class":39},"    \u002F\u002F ...other slices\n",[33,3940,3941],{"class":35,"line":325},[33,3942,2907],{"class":98},[33,3944,3945,3948,3950,3953,3955,3957,3960,3962,3965],{"class":35,"line":331},[33,3946,3947],{"class":94},"  middleware",[33,3949,1149],{"class":98},[33,3951,3952],{"class":125},"getDefault",[33,3954,1155],{"class":98},[33,3956,356],{"class":83},[33,3958,3959],{"class":94}," getDefault",[33,3961,2161],{"class":98},[33,3963,3964],{"class":94},"concat",[33,3966,3967],{"class":98},"(productsApi.middleware),\n",[33,3969,3970],{"class":35,"line":337},[33,3971,2912],{"class":98},[33,3973,3974],{"class":35,"line":397},[33,3975,77],{"emptyLinePlaceholder":76},[33,3977,3978,3980,3982],{"class":35,"line":961},[33,3979,116],{"class":83},[33,3981,3484],{"class":94},[33,3983,291],{"class":98},[33,3985,3986,3988,3990,3993,3995,3998,4000,4002,4004,4006,4009],{"class":35,"line":967},[33,3987,135],{"class":83},[33,3989,299],{"class":98},[33,3991,3992],{"class":87},"data",[33,3994,144],{"class":98},[33,3996,3997],{"class":87},"isLoading",[33,3999,144],{"class":98},[33,4001,3503],{"class":87},[33,4003,308],{"class":98},[33,4005,153],{"class":83},[33,4007,4008],{"class":94}," useGetProductsQuery",[33,4010,1121],{"class":98},[33,4012,4013,4015,4018,4020,4022,4024],{"class":35,"line":977},[33,4014,2141],{"class":83},[33,4016,4017],{"class":98}," (isLoading) ",[33,4019,3595],{"class":83},[33,4021,254],{"class":98},[33,4023,3600],{"class":87},[33,4025,3603],{"class":98},[33,4027,4028,4030,4033,4035,4037,4039,4041,4043],{"class":35,"line":992},[33,4029,2141],{"class":83},[33,4031,4032],{"class":98}," (error) ",[33,4034,3595],{"class":83},[33,4036,254],{"class":98},[33,4038,3623],{"class":87},[33,4040,3626],{"class":94},[33,4042,153],{"class":83},[33,4044,3631],{"class":98},[33,4046,4047,4049,4051,4053,4056,4058,4060,4062,4064,4066,4068,4070,4072,4074,4076,4078,4080],{"class":35,"line":1024},[33,4048,251],{"class":83},[33,4050,254],{"class":98},[33,4052,3640],{"class":344},[33,4054,4055],{"class":98},">{data.",[33,4057,3645],{"class":94},[33,4059,99],{"class":98},[33,4061,3650],{"class":125},[33,4063,367],{"class":83},[33,4065,254],{"class":98},[33,4067,3657],{"class":344},[33,4069,3660],{"class":94},[33,4071,153],{"class":83},[33,4073,3665],{"class":98},[33,4075,3657],{"class":344},[33,4077,3670],{"class":98},[33,4079,3640],{"class":344},[33,4081,269],{"class":98},[33,4083,4084],{"class":35,"line":1393},[33,4085,275],{"class":98},[14,4087,4089],{"id":4088},"jotai-atomic-bottom-up-state","Jotai: Atomic, Bottom-Up State",[19,4091,4093],{"filename":4092,"language":22},"jotai_basic.js",[24,4094,4096],{"className":26,"code":4095,"language":22,"meta":28,"style":28},"\u002F\u002F Jotai inverts the \"one big store\" shape: state is composed from many small,\n\u002F\u002F independent ATOMS, each an isolated unit of state that components subscribe\n\u002F\u002F to individually — closer in spirit to useState than to a global store.\n\nimport { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'\n\nconst cartItemsAtom = atom([])\n\n\u002F\u002F A derived\u002Fcomputed atom — automatically recalculates and notifies subscribers\n\u002F\u002F only when cartItemsAtom actually changes. Like useMemo but at the state layer.\nconst cartCountAtom = atom((get) => get(cartItemsAtom).length)\nconst cartTotalAtom = atom((get) =>\n  get(cartItemsAtom).reduce((sum, item) => sum + item.price, 0)\n)\n\nfunction CartSummary() {\n  \u002F\u002F Subscribing to cartCountAtom means this component never re-renders due to\n  \u002F\u002F changes elsewhere that don't affect the derived item count.\n  const [count] = useAtom(cartCountAtom)\n  return \u003Cspan>{count} items\u003C\u002Fspan>\n}\n\nfunction CartTotal() {\n  const total = useAtomValue(cartTotalAtom)  \u002F\u002F read-only subscription\n  return \u003Cspan>${total.toFixed(2)}\u003C\u002Fspan>\n}\n\nfunction AddToCartButton({ item }) {\n  const setItems = useSetAtom(cartItemsAtom)  \u002F\u002F write-only, no subscription to value\n  return \u003Cbutton onClick={() => setItems(prev => [...prev, item])}>Add\u003C\u002Fbutton>\n}\n",[30,4097,4098,4103,4108,4113,4117,4129,4133,4147,4151,4156,4161,4189,4209,4240,4244,4248,4256,4261,4266,4285,4300,4304,4308,4317,4335,4361,4365,4369,4382,4400,4436],{"__ignoreMap":28},[33,4099,4100],{"class":35,"line":36},[33,4101,4102],{"class":39},"\u002F\u002F Jotai inverts the \"one big store\" shape: state is composed from many small,\n",[33,4104,4105],{"class":35,"line":43},[33,4106,4107],{"class":39},"\u002F\u002F independent ATOMS, each an isolated unit of state that components subscribe\n",[33,4109,4110],{"class":35,"line":49},[33,4111,4112],{"class":39},"\u002F\u002F to individually — closer in spirit to useState than to a global store.\n",[33,4114,4115],{"class":35,"line":55},[33,4116,77],{"emptyLinePlaceholder":76},[33,4118,4119,4121,4124,4126],{"class":35,"line":61},[33,4120,1057],{"class":83},[33,4122,4123],{"class":98}," { atom, useAtom, useAtomValue, useSetAtom } ",[33,4125,1063],{"class":83},[33,4127,4128],{"class":188}," 'jotai'\n",[33,4130,4131],{"class":35,"line":67},[33,4132,77],{"emptyLinePlaceholder":76},[33,4134,4135,4137,4140,4142,4145],{"class":35,"line":73},[33,4136,84],{"class":83},[33,4138,4139],{"class":87}," cartItemsAtom",[33,4141,91],{"class":83},[33,4143,4144],{"class":94}," atom",[33,4146,215],{"class":98},[33,4148,4149],{"class":35,"line":80},[33,4150,77],{"emptyLinePlaceholder":76},[33,4152,4153],{"class":35,"line":108},[33,4154,4155],{"class":39},"\u002F\u002F A derived\u002Fcomputed atom — automatically recalculates and notifies subscribers\n",[33,4157,4158],{"class":35,"line":113},[33,4159,4160],{"class":39},"\u002F\u002F only when cartItemsAtom actually changes. Like useMemo but at the state layer.\n",[33,4162,4163,4165,4168,4170,4172,4174,4176,4178,4180,4182,4185,4187],{"class":35,"line":132},[33,4164,84],{"class":83},[33,4166,4167],{"class":87}," cartCountAtom",[33,4169,91],{"class":83},[33,4171,4144],{"class":94},[33,4173,1778],{"class":98},[33,4175,1786],{"class":125},[33,4177,1155],{"class":98},[33,4179,356],{"class":83},[33,4181,1904],{"class":94},[33,4183,4184],{"class":98},"(cartItemsAtom).",[33,4186,2003],{"class":87},[33,4188,105],{"class":98},[33,4190,4191,4193,4196,4198,4200,4202,4204,4206],{"class":35,"line":165},[33,4192,84],{"class":83},[33,4194,4195],{"class":87}," cartTotalAtom",[33,4197,91],{"class":83},[33,4199,4144],{"class":94},[33,4201,1778],{"class":98},[33,4203,1786],{"class":125},[33,4205,1155],{"class":98},[33,4207,4208],{"class":83},"=>\n",[33,4210,4211,4214,4216,4218,4220,4222,4224,4226,4228,4230,4232,4234,4236,4238],{"class":35,"line":194},[33,4212,4213],{"class":94},"  get",[33,4215,4184],{"class":98},[33,4217,1910],{"class":94},[33,4219,1778],{"class":98},[33,4221,1915],{"class":125},[33,4223,144],{"class":98},[33,4225,1808],{"class":125},[33,4227,1155],{"class":98},[33,4229,356],{"class":83},[33,4231,1926],{"class":98},[33,4233,1929],{"class":83},[33,4235,1932],{"class":98},[33,4237,1935],{"class":87},[33,4239,105],{"class":98},[33,4241,4242],{"class":35,"line":218},[33,4243,105],{"class":98},[33,4245,4246],{"class":35,"line":223},[33,4247,77],{"emptyLinePlaceholder":76},[33,4249,4250,4252,4254],{"class":35,"line":229},[33,4251,116],{"class":83},[33,4253,1968],{"class":94},[33,4255,291],{"class":98},[33,4257,4258],{"class":35,"line":235},[33,4259,4260],{"class":39},"  \u002F\u002F Subscribing to cartCountAtom means this component never re-renders due to\n",[33,4262,4263],{"class":35,"line":248},[33,4264,4265],{"class":39},"  \u002F\u002F changes elsewhere that don't affect the derived item count.\n",[33,4267,4268,4270,4272,4275,4277,4279,4282],{"class":35,"line":272},[33,4269,135],{"class":83},[33,4271,138],{"class":98},[33,4273,4274],{"class":87},"count",[33,4276,150],{"class":98},[33,4278,153],{"class":83},[33,4280,4281],{"class":94}," useAtom",[33,4283,4284],{"class":98},"(cartCountAtom)\n",[33,4286,4287,4289,4291,4293,4296,4298],{"class":35,"line":278},[33,4288,251],{"class":83},[33,4290,254],{"class":98},[33,4292,33],{"class":344},[33,4294,4295],{"class":98},">{count} items\u003C\u002F",[33,4297,33],{"class":344},[33,4299,269],{"class":98},[33,4301,4302],{"class":35,"line":283},[33,4303,275],{"class":98},[33,4305,4306],{"class":35,"line":294},[33,4307,77],{"emptyLinePlaceholder":76},[33,4309,4310,4312,4315],{"class":35,"line":319},[33,4311,116],{"class":83},[33,4313,4314],{"class":94}," CartTotal",[33,4316,291],{"class":98},[33,4318,4319,4321,4324,4326,4329,4332],{"class":35,"line":325},[33,4320,135],{"class":83},[33,4322,4323],{"class":87}," total",[33,4325,91],{"class":83},[33,4327,4328],{"class":94}," useAtomValue",[33,4330,4331],{"class":98},"(cartTotalAtom)  ",[33,4333,4334],{"class":39},"\u002F\u002F read-only subscription\n",[33,4336,4337,4339,4341,4343,4346,4349,4351,4354,4357,4359],{"class":35,"line":331},[33,4338,251],{"class":83},[33,4340,254],{"class":98},[33,4342,33],{"class":344},[33,4344,4345],{"class":98},">${total.",[33,4347,4348],{"class":94},"toFixed",[33,4350,99],{"class":98},[33,4352,4353],{"class":87},"2",[33,4355,4356],{"class":98},")}\u003C\u002F",[33,4358,33],{"class":344},[33,4360,269],{"class":98},[33,4362,4363],{"class":35,"line":337},[33,4364,275],{"class":98},[33,4366,4367],{"class":35,"line":397},[33,4368,77],{"emptyLinePlaceholder":76},[33,4370,4371,4373,4376,4378,4380],{"class":35,"line":961},[33,4372,116],{"class":83},[33,4374,4375],{"class":94}," AddToCartButton",[33,4377,122],{"class":98},[33,4379,1808],{"class":125},[33,4381,129],{"class":98},[33,4383,4384,4386,4389,4391,4394,4397],{"class":35,"line":967},[33,4385,135],{"class":83},[33,4387,4388],{"class":87}," setItems",[33,4390,91],{"class":83},[33,4392,4393],{"class":94}," useSetAtom",[33,4395,4396],{"class":98},"(cartItemsAtom)  ",[33,4398,4399],{"class":39},"\u002F\u002F write-only, no subscription to value\n",[33,4401,4402,4404,4406,4408,4410,4412,4414,4416,4418,4420,4423,4425,4427,4429,4432,4434],{"class":35,"line":977},[33,4403,251],{"class":83},[33,4405,254],{"class":98},[33,4407,345],{"class":344},[33,4409,348],{"class":94},[33,4411,153],{"class":83},[33,4413,353],{"class":98},[33,4415,356],{"class":83},[33,4417,4388],{"class":94},[33,4419,99],{"class":98},[33,4421,4422],{"class":125},"prev",[33,4424,367],{"class":83},[33,4426,138],{"class":98},[33,4428,1483],{"class":83},[33,4430,4431],{"class":98},"prev, item])}>Add\u003C\u002F",[33,4433,345],{"class":344},[33,4435,269],{"class":98},[33,4437,4438],{"class":35,"line":992},[33,4439,275],{"class":98},[401,4441,4443],{"id":4442},"jotai-derived-atoms-and-async","Jotai: Derived Atoms and Async",[19,4445,4447],{"filename":4446,"language":22},"jotai_advanced.js",[24,4448,4450],{"className":26,"code":4449,"language":22,"meta":28,"style":28},"import { atom, useAtom } from 'jotai'\n\n\u002F\u002F Async derived atom — Jotai handles the suspense automatically:\nconst userAtom = atom(null)\nconst userPostsAtom = atom(async (get) => {\n  const user = get(userAtom)\n  if (!user) return []\n  const res = await fetch(`\u002Fapi\u002Fusers\u002F${user.id}\u002Fposts`)\n  return res.json()\n})\n\nfunction UserPosts() {\n  const [posts] = useAtom(userPostsAtom)\n  \u002F\u002F The component suspends while the async atom resolves — wrap in \u003CSuspense>.\n  return \u003Cul>{posts.map(p => \u003Cli key={p.id}>{p.title}\u003C\u002Fli>)}\u003C\u002Ful>\n}\n\n\u002F\u002F Atom with write-only side effects:\nconst addToCartAtom = atom(null, (get, set, item) => {\n  const items = get(cartItemsAtom)\n  set(cartItemsAtom, [...items, item])\n  \u002F\u002F Can update MULTIPLE atoms in one write — atomic update across atoms:\n  set(lastAddedItemAtom, item)\n  set(cartModifiedAtAtom, Date.now())\n})\n",[30,4451,4452,4463,4467,4472,4489,4514,4528,4543,4571,4581,4585,4589,4598,4616,4621,4659,4663,4667,4672,4706,4719,4732,4737,4744,4756],{"__ignoreMap":28},[33,4453,4454,4456,4459,4461],{"class":35,"line":36},[33,4455,1057],{"class":83},[33,4457,4458],{"class":98}," { atom, useAtom } ",[33,4460,1063],{"class":83},[33,4462,4128],{"class":188},[33,4464,4465],{"class":35,"line":43},[33,4466,77],{"emptyLinePlaceholder":76},[33,4468,4469],{"class":35,"line":49},[33,4470,4471],{"class":39},"\u002F\u002F Async derived atom — Jotai handles the suspense automatically:\n",[33,4473,4474,4476,4479,4481,4483,4485,4487],{"class":35,"line":55},[33,4475,84],{"class":83},[33,4477,4478],{"class":87}," userAtom",[33,4480,91],{"class":83},[33,4482,4144],{"class":94},[33,4484,99],{"class":98},[33,4486,102],{"class":87},[33,4488,105],{"class":98},[33,4490,4491,4493,4496,4498,4500,4502,4504,4506,4508,4510,4512],{"class":35,"line":61},[33,4492,84],{"class":83},[33,4494,4495],{"class":87}," userPostsAtom",[33,4497,91],{"class":83},[33,4499,4144],{"class":94},[33,4501,99],{"class":98},[33,4503,3187],{"class":83},[33,4505,3222],{"class":98},[33,4507,1786],{"class":125},[33,4509,1155],{"class":98},[33,4511,356],{"class":83},[33,4513,1128],{"class":98},[33,4515,4516,4518,4521,4523,4525],{"class":35,"line":67},[33,4517,135],{"class":83},[33,4519,4520],{"class":87}," user",[33,4522,91],{"class":83},[33,4524,1904],{"class":94},[33,4526,4527],{"class":98},"(userAtom)\n",[33,4529,4530,4532,4534,4536,4539,4541],{"class":35,"line":73},[33,4531,2141],{"class":83},[33,4533,3222],{"class":98},[33,4535,3225],{"class":83},[33,4537,4538],{"class":98},"user) ",[33,4540,3595],{"class":83},[33,4542,2898],{"class":98},[33,4544,4545,4547,4549,4551,4553,4555,4557,4560,4562,4564,4566,4569],{"class":35,"line":80},[33,4546,135],{"class":83},[33,4548,3200],{"class":87},[33,4550,91],{"class":83},[33,4552,3205],{"class":83},[33,4554,3208],{"class":94},[33,4556,99],{"class":98},[33,4558,4559],{"class":188},"`\u002Fapi\u002Fusers\u002F${",[33,4561,141],{"class":98},[33,4563,3247],{"class":188},[33,4565,1840],{"class":98},[33,4567,4568],{"class":188},"}\u002Fposts`",[33,4570,105],{"class":98},[33,4572,4573,4575,4577,4579],{"class":35,"line":108},[33,4574,251],{"class":83},[33,4576,3261],{"class":98},[33,4578,3264],{"class":94},[33,4580,1121],{"class":98},[33,4582,4583],{"class":35,"line":113},[33,4584,2912],{"class":98},[33,4586,4587],{"class":35,"line":132},[33,4588,77],{"emptyLinePlaceholder":76},[33,4590,4591,4593,4596],{"class":35,"line":165},[33,4592,116],{"class":83},[33,4594,4595],{"class":94}," UserPosts",[33,4597,291],{"class":98},[33,4599,4600,4602,4604,4607,4609,4611,4613],{"class":35,"line":194},[33,4601,135],{"class":83},[33,4603,138],{"class":98},[33,4605,4606],{"class":87},"posts",[33,4608,150],{"class":98},[33,4610,153],{"class":83},[33,4612,4281],{"class":94},[33,4614,4615],{"class":98},"(userPostsAtom)\n",[33,4617,4618],{"class":35,"line":218},[33,4619,4620],{"class":39},"  \u002F\u002F The component suspends while the async atom resolves — wrap in \u003CSuspense>.\n",[33,4622,4623,4625,4627,4629,4632,4634,4636,4638,4640,4642,4644,4646,4648,4651,4653,4655,4657],{"class":35,"line":223},[33,4624,251],{"class":83},[33,4626,254],{"class":98},[33,4628,3640],{"class":344},[33,4630,4631],{"class":98},">{posts.",[33,4633,3645],{"class":94},[33,4635,99],{"class":98},[33,4637,3650],{"class":125},[33,4639,367],{"class":83},[33,4641,254],{"class":98},[33,4643,3657],{"class":344},[33,4645,3660],{"class":94},[33,4647,153],{"class":83},[33,4649,4650],{"class":98},"{p.id}>{p.title}\u003C\u002F",[33,4652,3657],{"class":344},[33,4654,3670],{"class":98},[33,4656,3640],{"class":344},[33,4658,269],{"class":98},[33,4660,4661],{"class":35,"line":229},[33,4662,275],{"class":98},[33,4664,4665],{"class":35,"line":235},[33,4666,77],{"emptyLinePlaceholder":76},[33,4668,4669],{"class":35,"line":248},[33,4670,4671],{"class":39},"\u002F\u002F Atom with write-only side effects:\n",[33,4673,4674,4676,4679,4681,4683,4685,4687,4690,4692,4694,4696,4698,4700,4702,4704],{"class":35,"line":272},[33,4675,84],{"class":83},[33,4677,4678],{"class":87}," addToCartAtom",[33,4680,91],{"class":83},[33,4682,4144],{"class":94},[33,4684,99],{"class":98},[33,4686,102],{"class":87},[33,4688,4689],{"class":98},", (",[33,4691,1786],{"class":125},[33,4693,144],{"class":98},[33,4695,1781],{"class":125},[33,4697,144],{"class":98},[33,4699,1808],{"class":125},[33,4701,1155],{"class":98},[33,4703,356],{"class":83},[33,4705,1128],{"class":98},[33,4707,4708,4710,4712,4714,4716],{"class":35,"line":278},[33,4709,135],{"class":83},[33,4711,2317],{"class":87},[33,4713,91],{"class":83},[33,4715,1904],{"class":94},[33,4717,4718],{"class":98},"(cartItemsAtom)\n",[33,4720,4721,4724,4727,4729],{"class":35,"line":283},[33,4722,4723],{"class":94},"  set",[33,4725,4726],{"class":98},"(cartItemsAtom, [",[33,4728,1483],{"class":83},[33,4730,4731],{"class":98},"items, item])\n",[33,4733,4734],{"class":35,"line":294},[33,4735,4736],{"class":39},"  \u002F\u002F Can update MULTIPLE atoms in one write — atomic update across atoms:\n",[33,4738,4739,4741],{"class":35,"line":319},[33,4740,4723],{"class":94},[33,4742,4743],{"class":98},"(lastAddedItemAtom, item)\n",[33,4745,4746,4748,4751,4754],{"class":35,"line":325},[33,4747,4723],{"class":94},[33,4749,4750],{"class":98},"(cartModifiedAtAtom, Date.",[33,4752,4753],{"class":94},"now",[33,4755,1212],{"class":98},[33,4757,4758],{"class":35,"line":331},[33,4759,2912],{"class":98},[14,4761,4763],{"id":4762},"comparison-table-context-vs-zustand-vs-redux-toolkit-vs-jotai","Comparison Table: Context vs Zustand vs Redux Toolkit vs Jotai",[19,4765,4767],{"filename":4766,"language":22},"comparison.js",[24,4768,4770],{"className":26,"code":4769,"language":22,"meta":28,"style":28},"\u002F\u002F | Feature                | Context           | Zustand          | Redux Toolkit       | Jotai              |\n\u002F\u002F |------------------------|-------------------|------------------|---------------------|--------------------|\n\u002F\u002F | Provider required?     | Yes               | No               | Yes                 | Optional           |\n\u002F\u002F | Bundle size            | 0 (built-in)      | ~1KB             | ~12KB               | ~3KB               |\n\u002F\u002F | Boilerplate            | Low               | Very low         | Medium (RTK reduces)| Low                |\n\u002F\u002F | Fine-grained subs      | No (all-or-noth)  | Yes (selectors)  | Yes (useSelector)   | Yes (per-atom)     |\n\u002F\u002F | Read outside React     | No                | Yes (getState)   | Yes (store.getState)| Yes (store.get)    |\n\u002F\u002F | DevTools\u002Ftime-travel   | No                | Yes (middleware) | Yes (excellent)     | Yes (limited)      |\n\u002F\u002F | Mental model           | DI + prop avoid   | Hook + singleton | Actions + reducers  | Atoms (bottom-up)  |\n\u002F\u002F | Derived state          | Manual useMemo    | Manual selectors | Reselect (selectors)| Built-in (atoms)   |\n\u002F\u002F | Async\u002Fside effects     | Manual (useEffect)| Manual           | createAsyncThunk    | Async atoms        |\n\u002F\u002F | Middleware ecosystem   | N\u002FA               | persist\u002Fimmer    | RTK Query, listener | Limited            |\n\u002F\u002F | Best for               | Infreq-changing   | Minimal ceremony | Large teams\u002Fstruct  | Many small pieces  |\n\u002F\u002F | Learning curve         | Lowest            | Low              | Medium              | Low-medium         |\n\u002F\u002F | State outside React    | Impossible        | Trivial          | Trivial             | Trivial            |\n\u002F\u002F | Testing                | renderWithProvider| Direct store     | Direct store        | Direct store       |\n",[30,4771,4772,4777,4782,4787,4792,4797,4802,4807,4812,4817,4822,4827,4832,4837,4842,4847],{"__ignoreMap":28},[33,4773,4774],{"class":35,"line":36},[33,4775,4776],{"class":39},"\u002F\u002F | Feature                | Context           | Zustand          | Redux Toolkit       | Jotai              |\n",[33,4778,4779],{"class":35,"line":43},[33,4780,4781],{"class":39},"\u002F\u002F |------------------------|-------------------|------------------|---------------------|--------------------|\n",[33,4783,4784],{"class":35,"line":49},[33,4785,4786],{"class":39},"\u002F\u002F | Provider required?     | Yes               | No               | Yes                 | Optional           |\n",[33,4788,4789],{"class":35,"line":55},[33,4790,4791],{"class":39},"\u002F\u002F | Bundle size            | 0 (built-in)      | ~1KB             | ~12KB               | ~3KB               |\n",[33,4793,4794],{"class":35,"line":61},[33,4795,4796],{"class":39},"\u002F\u002F | Boilerplate            | Low               | Very low         | Medium (RTK reduces)| Low                |\n",[33,4798,4799],{"class":35,"line":67},[33,4800,4801],{"class":39},"\u002F\u002F | Fine-grained subs      | No (all-or-noth)  | Yes (selectors)  | Yes (useSelector)   | Yes (per-atom)     |\n",[33,4803,4804],{"class":35,"line":73},[33,4805,4806],{"class":39},"\u002F\u002F | Read outside React     | No                | Yes (getState)   | Yes (store.getState)| Yes (store.get)    |\n",[33,4808,4809],{"class":35,"line":80},[33,4810,4811],{"class":39},"\u002F\u002F | DevTools\u002Ftime-travel   | No                | Yes (middleware) | Yes (excellent)     | Yes (limited)      |\n",[33,4813,4814],{"class":35,"line":108},[33,4815,4816],{"class":39},"\u002F\u002F | Mental model           | DI + prop avoid   | Hook + singleton | Actions + reducers  | Atoms (bottom-up)  |\n",[33,4818,4819],{"class":35,"line":113},[33,4820,4821],{"class":39},"\u002F\u002F | Derived state          | Manual useMemo    | Manual selectors | Reselect (selectors)| Built-in (atoms)   |\n",[33,4823,4824],{"class":35,"line":132},[33,4825,4826],{"class":39},"\u002F\u002F | Async\u002Fside effects     | Manual (useEffect)| Manual           | createAsyncThunk    | Async atoms        |\n",[33,4828,4829],{"class":35,"line":165},[33,4830,4831],{"class":39},"\u002F\u002F | Middleware ecosystem   | N\u002FA               | persist\u002Fimmer    | RTK Query, listener | Limited            |\n",[33,4833,4834],{"class":35,"line":194},[33,4835,4836],{"class":39},"\u002F\u002F | Best for               | Infreq-changing   | Minimal ceremony | Large teams\u002Fstruct  | Many small pieces  |\n",[33,4838,4839],{"class":35,"line":218},[33,4840,4841],{"class":39},"\u002F\u002F | Learning curve         | Lowest            | Low              | Medium              | Low-medium         |\n",[33,4843,4844],{"class":35,"line":223},[33,4845,4846],{"class":39},"\u002F\u002F | State outside React    | Impossible        | Trivial          | Trivial             | Trivial            |\n",[33,4848,4849],{"class":35,"line":229},[33,4850,4851],{"class":39},"\u002F\u002F | Testing                | renderWithProvider| Direct store     | Direct store        | Direct store       |\n",[14,4853,4855],{"id":4854},"server-state-vs-client-state","Server State vs Client State",[19,4857,4859],{"filename":4858,"language":22},"server_vs_client.js",[24,4860,4862],{"className":26,"code":4861,"language":22,"meta":28,"style":28},"\u002F\u002F THE most common conceptual mistake: conflating server state with client state.\n\u002F\u002F They have fundamentally different characteristics and need different tools.\n\n\u002F\u002F SERVER STATE (API responses, database queries):\n\u002F\u002F - Owned by the server, not the client\n\u002F\u002F - Can be stale (the server data may have changed since you fetched it)\n\u002F\u002F - Needs caching, refetching, invalidation, optimistic updates\n\u002F\u002F - Multiple components may need the same data (deduplication)\n\u002F\u002F - Race conditions, pagination, background updates\n\u002F\u002F → Use React Query, SWR, or RTK Query\n\n\u002F\u002F CLIENT STATE (UI state, form drafts, preferences):\n\u002F\u002F - Owned by the client\n\u002F\u002F - Not stale (it IS the source of truth while the app runs)\n\u002F\u002F - No refetching\u002Finvalidation needed\n\u002F\u002F - Usually component-local or shared via Context\u002Fstore\n\u002F\u002F → Use useState, Context, Zustand, Redux, or Jotai\n\n\u002F\u002F CORRECT separation:\nfunction App() {\n  return (\n    \u003CQueryClientProvider client={queryClient}>\n      {\u002F* Server state: React Query *\u002F}\n      \u003CUserDashboard \u002F>\n\n      {\u002F* Client state: Zustand for UI-only state *\u002F}\n      \u003CUIProvider>\n        \u003CSidebar \u002F>\n        \u003CModals \u002F>\n      \u003C\u002FUIProvider>\n    \u003C\u002FQueryClientProvider>\n  )\n}\n\n\u002F\u002F ANTI-PATTERN: storing API responses in Redux\u002FZustand and manually keeping\n\u002F\u002F them in sync — you're reinventing a worse version of React Query inside your\n\u002F\u002F state library, complete with hand-rolled caching, invalidation, and race conditions.\n\n\u002F\u002F ANTI-PATTERN: using React Query for genuinely client-only state like\n\u002F\u002F \"is the sidebar open\" — it's not server data, doesn't need caching, and\n\u002F\u002F you're adding network-fetching semantics to local UI state.\n",[30,4863,4864,4869,4874,4878,4883,4888,4893,4898,4903,4908,4913,4917,4922,4927,4932,4937,4942,4947,4951,4956,4965,4971,4986,4996,5005,5009,5018,5027,5036,5045,5053,5061,5065,5069,5073,5078,5083,5088,5092,5097,5102],{"__ignoreMap":28},[33,4865,4866],{"class":35,"line":36},[33,4867,4868],{"class":39},"\u002F\u002F THE most common conceptual mistake: conflating server state with client state.\n",[33,4870,4871],{"class":35,"line":43},[33,4872,4873],{"class":39},"\u002F\u002F They have fundamentally different characteristics and need different tools.\n",[33,4875,4876],{"class":35,"line":49},[33,4877,77],{"emptyLinePlaceholder":76},[33,4879,4880],{"class":35,"line":55},[33,4881,4882],{"class":39},"\u002F\u002F SERVER STATE (API responses, database queries):\n",[33,4884,4885],{"class":35,"line":61},[33,4886,4887],{"class":39},"\u002F\u002F - Owned by the server, not the client\n",[33,4889,4890],{"class":35,"line":67},[33,4891,4892],{"class":39},"\u002F\u002F - Can be stale (the server data may have changed since you fetched it)\n",[33,4894,4895],{"class":35,"line":73},[33,4896,4897],{"class":39},"\u002F\u002F - Needs caching, refetching, invalidation, optimistic updates\n",[33,4899,4900],{"class":35,"line":80},[33,4901,4902],{"class":39},"\u002F\u002F - Multiple components may need the same data (deduplication)\n",[33,4904,4905],{"class":35,"line":108},[33,4906,4907],{"class":39},"\u002F\u002F - Race conditions, pagination, background updates\n",[33,4909,4910],{"class":35,"line":113},[33,4911,4912],{"class":39},"\u002F\u002F → Use React Query, SWR, or RTK Query\n",[33,4914,4915],{"class":35,"line":132},[33,4916,77],{"emptyLinePlaceholder":76},[33,4918,4919],{"class":35,"line":165},[33,4920,4921],{"class":39},"\u002F\u002F CLIENT STATE (UI state, form drafts, preferences):\n",[33,4923,4924],{"class":35,"line":194},[33,4925,4926],{"class":39},"\u002F\u002F - Owned by the client\n",[33,4928,4929],{"class":35,"line":218},[33,4930,4931],{"class":39},"\u002F\u002F - Not stale (it IS the source of truth while the app runs)\n",[33,4933,4934],{"class":35,"line":223},[33,4935,4936],{"class":39},"\u002F\u002F - No refetching\u002Finvalidation needed\n",[33,4938,4939],{"class":35,"line":229},[33,4940,4941],{"class":39},"\u002F\u002F - Usually component-local or shared via Context\u002Fstore\n",[33,4943,4944],{"class":35,"line":235},[33,4945,4946],{"class":39},"\u002F\u002F → Use useState, Context, Zustand, Redux, or Jotai\n",[33,4948,4949],{"class":35,"line":248},[33,4950,77],{"emptyLinePlaceholder":76},[33,4952,4953],{"class":35,"line":272},[33,4954,4955],{"class":39},"\u002F\u002F CORRECT separation:\n",[33,4957,4958,4960,4963],{"class":35,"line":278},[33,4959,116],{"class":83},[33,4961,4962],{"class":94}," App",[33,4964,291],{"class":98},[33,4966,4967,4969],{"class":35,"line":283},[33,4968,251],{"class":83},[33,4970,505],{"class":98},[33,4972,4973,4975,4978,4981,4983],{"class":35,"line":294},[33,4974,510],{"class":98},[33,4976,4977],{"class":87},"QueryClientProvider",[33,4979,4980],{"class":94}," client",[33,4982,153],{"class":83},[33,4984,4985],{"class":98},"{queryClient}>\n",[33,4987,4988,4991,4994],{"class":35,"line":319},[33,4989,4990],{"class":98},"      {",[33,4992,4993],{"class":39},"\u002F* Server state: React Query *\u002F",[33,4995,275],{"class":98},[33,4997,4998,5000,5003],{"class":35,"line":325},[33,4999,535],{"class":98},[33,5001,5002],{"class":87},"UserDashboard",[33,5004,3603],{"class":98},[33,5006,5007],{"class":35,"line":331},[33,5008,77],{"emptyLinePlaceholder":76},[33,5010,5011,5013,5016],{"class":35,"line":337},[33,5012,4990],{"class":98},[33,5014,5015],{"class":39},"\u002F* Client state: Zustand for UI-only state *\u002F",[33,5017,275],{"class":98},[33,5019,5020,5022,5025],{"class":35,"line":397},[33,5021,535],{"class":98},[33,5023,5024],{"class":87},"UIProvider",[33,5026,269],{"class":98},[33,5028,5029,5031,5034],{"class":35,"line":961},[33,5030,557],{"class":98},[33,5032,5033],{"class":87},"Sidebar",[33,5035,3603],{"class":98},[33,5037,5038,5040,5043],{"class":35,"line":967},[33,5039,557],{"class":98},[33,5041,5042],{"class":87},"Modals",[33,5044,3603],{"class":98},[33,5046,5047,5049,5051],{"class":35,"line":977},[33,5048,590],{"class":98},[33,5050,5024],{"class":87},[33,5052,269],{"class":98},[33,5054,5055,5057,5059],{"class":35,"line":992},[33,5056,599],{"class":98},[33,5058,4977],{"class":87},[33,5060,269],{"class":98},[33,5062,5063],{"class":35,"line":1024},[33,5064,608],{"class":98},[33,5066,5067],{"class":35,"line":1393},[33,5068,275],{"class":98},[33,5070,5071],{"class":35,"line":1399},[33,5072,77],{"emptyLinePlaceholder":76},[33,5074,5075],{"class":35,"line":1405},[33,5076,5077],{"class":39},"\u002F\u002F ANTI-PATTERN: storing API responses in Redux\u002FZustand and manually keeping\n",[33,5079,5080],{"class":35,"line":1420},[33,5081,5082],{"class":39},"\u002F\u002F them in sync — you're reinventing a worse version of React Query inside your\n",[33,5084,5085],{"class":35,"line":1426},[33,5086,5087],{"class":39},"\u002F\u002F state library, complete with hand-rolled caching, invalidation, and race conditions.\n",[33,5089,5090],{"class":35,"line":1446},[33,5091,77],{"emptyLinePlaceholder":76},[33,5093,5094],{"class":35,"line":1451},[33,5095,5096],{"class":39},"\u002F\u002F ANTI-PATTERN: using React Query for genuinely client-only state like\n",[33,5098,5099],{"class":35,"line":1508},[33,5100,5101],{"class":39},"\u002F\u002F \"is the sidebar open\" — it's not server data, doesn't need caching, and\n",[33,5103,5104],{"class":35,"line":3050},[33,5105,5106],{"class":39},"\u002F\u002F you're adding network-fetching semantics to local UI state.\n",[14,5108,5110],{"id":5109},"a-concrete-migration-context-ceiling-zustand","A Concrete Migration: Context Ceiling → Zustand",[19,5112,5114],{"filename":5113,"language":22},"migration.js",[24,5115,5117],{"className":26,"code":5116,"language":22,"meta":28,"style":28},"\u002F\u002F BEFORE: one big Context value, every consumer re-renders on any field change\nconst AppContext = createContext(null)\n\nfunction AppProvider({ children }) {\n  const [user, setUser] = useState(null)\n  const [cart, setCart] = useState([])\n  const [notifications, setNotifications] = useState([])\n\n  return (\n    \u003CAppContext.Provider value={{ user, setUser, cart, setCart, notifications, setNotifications }}>\n      {children}\n    \u003C\u002FAppContext.Provider>\n  )\n}\n\nfunction ThemeToggle() {\n  const { user } = useContext(AppContext)\n  \u002F\u002F Re-renders when cart OR notifications change, even though it only reads user.\n  return \u003Cspan>{user?.name}\u003C\u002Fspan>\n}\n\n\u002F\u002F AFTER: three independent Zustand stores, each with its own fine-grained subscribers\nimport { create } from 'zustand'\n\nconst useUserStore = create((set) => ({\n  user: null,\n  setUser: (user) => set({ user }),\n}))\n\nconst useCartStore = create((set) => ({\n  cart: [],\n  addToCart: (item) => set(state => ({ cart: [...state.cart, item] })),\n  removeFromCart: (id) => set(state => ({ cart: state.cart.filter(i => i.id !== id) })),\n}))\n\nconst useNotificationStore = create((set) => ({\n  notifications: [],\n  add: (n) => set(state => ({ notifications: [...state.notifications, n] })),\n  clear: () => set({ notifications: [] }),\n}))\n\n\u002F\u002F No \u003CProvider> needed anywhere — components import and call these hooks directly.\nfunction UserBadge() {\n  \u002F\u002F Reads NOTHING from cart or notifications — genuinely never re-renders on\n  \u002F\u002F their changes. Only re-renders when `user` changes.\n  const user = useUserStore(state => state.user)\n  return \u003Cspan>{user?.name}\u003C\u002Fspan>\n}\n\n\u002F\u002F The migration's real payoff: each store is independently subscribable,\n\u002F\u002F removing BOTH of Context's structural limitations (coarse re-renders, no\n\u002F\u002F non-component access) in one step, without Redux's action\u002Freducer ceremony.\n",[30,5118,5119,5124,5140,5144,5156,5180,5202,5222,5226,5232,5245,5250,5258,5262,5266,5270,5278,5294,5299,5314,5318,5322,5327,5337,5341,5362,5371,5389,5393,5397,5417,5422,5451,5491,5495,5499,5520,5525,5555,5569,5573,5577,5582,5591,5596,5601,5620,5634,5639,5644,5650,5656],{"__ignoreMap":28},[33,5120,5121],{"class":35,"line":36},[33,5122,5123],{"class":39},"\u002F\u002F BEFORE: one big Context value, every consumer re-renders on any field change\n",[33,5125,5126,5128,5130,5132,5134,5136,5138],{"class":35,"line":43},[33,5127,84],{"class":83},[33,5129,88],{"class":87},[33,5131,91],{"class":83},[33,5133,95],{"class":94},[33,5135,99],{"class":98},[33,5137,102],{"class":87},[33,5139,105],{"class":98},[33,5141,5142],{"class":35,"line":49},[33,5143,77],{"emptyLinePlaceholder":76},[33,5145,5146,5148,5150,5152,5154],{"class":35,"line":55},[33,5147,116],{"class":83},[33,5149,119],{"class":94},[33,5151,122],{"class":98},[33,5153,126],{"class":125},[33,5155,129],{"class":98},[33,5157,5158,5160,5162,5164,5166,5168,5170,5172,5174,5176,5178],{"class":35,"line":61},[33,5159,135],{"class":83},[33,5161,138],{"class":98},[33,5163,141],{"class":87},[33,5165,144],{"class":98},[33,5167,147],{"class":87},[33,5169,150],{"class":98},[33,5171,153],{"class":83},[33,5173,156],{"class":94},[33,5175,99],{"class":98},[33,5177,102],{"class":87},[33,5179,105],{"class":98},[33,5181,5182,5184,5186,5189,5191,5194,5196,5198,5200],{"class":35,"line":67},[33,5183,135],{"class":83},[33,5185,138],{"class":98},[33,5187,5188],{"class":87},"cart",[33,5190,144],{"class":98},[33,5192,5193],{"class":87},"setCart",[33,5195,150],{"class":98},[33,5197,153],{"class":83},[33,5199,156],{"class":94},[33,5201,215],{"class":98},[33,5203,5204,5206,5208,5210,5212,5214,5216,5218,5220],{"class":35,"line":73},[33,5205,135],{"class":83},[33,5207,138],{"class":98},[33,5209,201],{"class":87},[33,5211,144],{"class":98},[33,5213,206],{"class":87},[33,5215,150],{"class":98},[33,5217,153],{"class":83},[33,5219,156],{"class":94},[33,5221,215],{"class":98},[33,5223,5224],{"class":35,"line":80},[33,5225,77],{"emptyLinePlaceholder":76},[33,5227,5228,5230],{"class":35,"line":108},[33,5229,251],{"class":83},[33,5231,505],{"class":98},[33,5233,5234,5236,5238,5240,5242],{"class":35,"line":113},[33,5235,510],{"class":98},[33,5237,257],{"class":87},[33,5239,240],{"class":94},[33,5241,153],{"class":83},[33,5243,5244],{"class":98},"{{ user, setUser, cart, setCart, notifications, setNotifications }}>\n",[33,5246,5247],{"class":35,"line":132},[33,5248,5249],{"class":98},"      {children}\n",[33,5251,5252,5254,5256],{"class":35,"line":165},[33,5253,599],{"class":98},[33,5255,257],{"class":87},[33,5257,269],{"class":98},[33,5259,5260],{"class":35,"line":194},[33,5261,608],{"class":98},[33,5263,5264],{"class":35,"line":218},[33,5265,275],{"class":98},[33,5267,5268],{"class":35,"line":223},[33,5269,77],{"emptyLinePlaceholder":76},[33,5271,5272,5274,5276],{"class":35,"line":229},[33,5273,116],{"class":83},[33,5275,288],{"class":94},[33,5277,291],{"class":98},[33,5279,5280,5282,5284,5286,5288,5290,5292],{"class":35,"line":235},[33,5281,135],{"class":83},[33,5283,299],{"class":98},[33,5285,141],{"class":87},[33,5287,308],{"class":98},[33,5289,153],{"class":83},[33,5291,313],{"class":94},[33,5293,316],{"class":98},[33,5295,5296],{"class":35,"line":248},[33,5297,5298],{"class":39},"  \u002F\u002F Re-renders when cart OR notifications change, even though it only reads user.\n",[33,5300,5301,5303,5305,5307,5310,5312],{"class":35,"line":272},[33,5302,251],{"class":83},[33,5304,254],{"class":98},[33,5306,33],{"class":344},[33,5308,5309],{"class":98},">{user?.name}\u003C\u002F",[33,5311,33],{"class":344},[33,5313,269],{"class":98},[33,5315,5316],{"class":35,"line":278},[33,5317,275],{"class":98},[33,5319,5320],{"class":35,"line":283},[33,5321,77],{"emptyLinePlaceholder":76},[33,5323,5324],{"class":35,"line":294},[33,5325,5326],{"class":39},"\u002F\u002F AFTER: three independent Zustand stores, each with its own fine-grained subscribers\n",[33,5328,5329,5331,5333,5335],{"class":35,"line":319},[33,5330,1057],{"class":83},[33,5332,1754],{"class":98},[33,5334,1063],{"class":83},[33,5336,1759],{"class":188},[33,5338,5339],{"class":35,"line":325},[33,5340,77],{"emptyLinePlaceholder":76},[33,5342,5343,5345,5348,5350,5352,5354,5356,5358,5360],{"class":35,"line":331},[33,5344,84],{"class":83},[33,5346,5347],{"class":87}," useUserStore",[33,5349,91],{"class":83},[33,5351,1775],{"class":94},[33,5353,1778],{"class":98},[33,5355,1781],{"class":125},[33,5357,1155],{"class":98},[33,5359,356],{"class":83},[33,5361,1793],{"class":98},[33,5363,5364,5367,5369],{"class":35,"line":337},[33,5365,5366],{"class":98},"  user: ",[33,5368,102],{"class":87},[33,5370,2567],{"class":98},[33,5372,5373,5376,5378,5380,5382,5384,5386],{"class":35,"line":397},[33,5374,5375],{"class":94},"  setUser",[33,5377,1149],{"class":98},[33,5379,141],{"class":125},[33,5381,1155],{"class":98},[33,5383,356],{"class":83},[33,5385,1815],{"class":94},[33,5387,5388],{"class":98},"({ user }),\n",[33,5390,5391],{"class":35,"line":961},[33,5392,1943],{"class":98},[33,5394,5395],{"class":35,"line":967},[33,5396,77],{"emptyLinePlaceholder":76},[33,5398,5399,5401,5403,5405,5407,5409,5411,5413,5415],{"class":35,"line":977},[33,5400,84],{"class":83},[33,5402,1770],{"class":87},[33,5404,91],{"class":83},[33,5406,1775],{"class":94},[33,5408,1778],{"class":98},[33,5410,1781],{"class":125},[33,5412,1155],{"class":98},[33,5414,356],{"class":83},[33,5416,1793],{"class":98},[33,5418,5419],{"class":35,"line":992},[33,5420,5421],{"class":98},"  cart: [],\n",[33,5423,5424,5427,5429,5431,5433,5435,5437,5439,5441,5443,5446,5448],{"class":35,"line":1024},[33,5425,5426],{"class":94},"  addToCart",[33,5428,1149],{"class":98},[33,5430,1808],{"class":125},[33,5432,1155],{"class":98},[33,5434,356],{"class":83},[33,5436,1815],{"class":94},[33,5438,99],{"class":98},[33,5440,1820],{"class":125},[33,5442,367],{"class":83},[33,5444,5445],{"class":98}," ({ cart: [",[33,5447,1483],{"class":83},[33,5449,5450],{"class":98},"state.cart, item] })),\n",[33,5452,5453,5456,5458,5460,5462,5464,5466,5468,5470,5472,5475,5477,5479,5482,5484,5487,5489],{"class":35,"line":1393},[33,5454,5455],{"class":94},"  removeFromCart",[33,5457,1149],{"class":98},[33,5459,1840],{"class":125},[33,5461,1155],{"class":98},[33,5463,356],{"class":83},[33,5465,1815],{"class":94},[33,5467,99],{"class":98},[33,5469,1820],{"class":125},[33,5471,367],{"class":83},[33,5473,5474],{"class":98}," ({ cart: state.cart.",[33,5476,1858],{"class":94},[33,5478,99],{"class":98},[33,5480,5481],{"class":125},"i",[33,5483,367],{"class":83},[33,5485,5486],{"class":98}," i.id ",[33,5488,1870],{"class":83},[33,5490,1873],{"class":98},[33,5492,5493],{"class":35,"line":1399},[33,5494,1943],{"class":98},[33,5496,5497],{"class":35,"line":1405},[33,5498,77],{"emptyLinePlaceholder":76},[33,5500,5501,5503,5506,5508,5510,5512,5514,5516,5518],{"class":35,"line":1420},[33,5502,84],{"class":83},[33,5504,5505],{"class":87}," useNotificationStore",[33,5507,91],{"class":83},[33,5509,1775],{"class":94},[33,5511,1778],{"class":98},[33,5513,1781],{"class":125},[33,5515,1155],{"class":98},[33,5517,356],{"class":83},[33,5519,1793],{"class":98},[33,5521,5522],{"class":35,"line":1426},[33,5523,5524],{"class":98},"  notifications: [],\n",[33,5526,5527,5530,5532,5535,5537,5539,5541,5543,5545,5547,5550,5552],{"class":35,"line":1446},[33,5528,5529],{"class":94},"  add",[33,5531,1149],{"class":98},[33,5533,5534],{"class":125},"n",[33,5536,1155],{"class":98},[33,5538,356],{"class":83},[33,5540,1815],{"class":94},[33,5542,99],{"class":98},[33,5544,1820],{"class":125},[33,5546,367],{"class":83},[33,5548,5549],{"class":98}," ({ notifications: [",[33,5551,1483],{"class":83},[33,5553,5554],{"class":98},"state.notifications, n] })),\n",[33,5556,5557,5560,5562,5564,5566],{"class":35,"line":1451},[33,5558,5559],{"class":94},"  clear",[33,5561,1136],{"class":98},[33,5563,356],{"class":83},[33,5565,1815],{"class":94},[33,5567,5568],{"class":98},"({ notifications: [] }),\n",[33,5570,5571],{"class":35,"line":1508},[33,5572,1943],{"class":98},[33,5574,5575],{"class":35,"line":3050},[33,5576,77],{"emptyLinePlaceholder":76},[33,5578,5579],{"class":35,"line":3057},[33,5580,5581],{"class":39},"\u002F\u002F No \u003CProvider> needed anywhere — components import and call these hooks directly.\n",[33,5583,5584,5586,5589],{"class":35,"line":3066},[33,5585,116],{"class":83},[33,5587,5588],{"class":94}," UserBadge",[33,5590,291],{"class":98},[33,5592,5593],{"class":35,"line":3079},[33,5594,5595],{"class":39},"  \u002F\u002F Reads NOTHING from cart or notifications — genuinely never re-renders on\n",[33,5597,5598],{"class":35,"line":3115},[33,5599,5600],{"class":39},"  \u002F\u002F their changes. Only re-renders when `user` changes.\n",[33,5602,5603,5605,5607,5609,5611,5613,5615,5617],{"class":35,"line":3124},[33,5604,135],{"class":83},[33,5606,4520],{"class":87},[33,5608,91],{"class":83},[33,5610,5347],{"class":94},[33,5612,99],{"class":98},[33,5614,1820],{"class":125},[33,5616,367],{"class":83},[33,5618,5619],{"class":98}," state.user)\n",[33,5621,5622,5624,5626,5628,5630,5632],{"class":35,"line":3129},[33,5623,251],{"class":83},[33,5625,254],{"class":98},[33,5627,33],{"class":344},[33,5629,5309],{"class":98},[33,5631,33],{"class":344},[33,5633,269],{"class":98},[33,5635,5637],{"class":35,"line":5636},48,[33,5638,275],{"class":98},[33,5640,5642],{"class":35,"line":5641},49,[33,5643,77],{"emptyLinePlaceholder":76},[33,5645,5647],{"class":35,"line":5646},50,[33,5648,5649],{"class":39},"\u002F\u002F The migration's real payoff: each store is independently subscribable,\n",[33,5651,5653],{"class":35,"line":5652},51,[33,5654,5655],{"class":39},"\u002F\u002F removing BOTH of Context's structural limitations (coarse re-renders, no\n",[33,5657,5659],{"class":35,"line":5658},52,[33,5660,5661],{"class":39},"\u002F\u002F non-component access) in one step, without Redux's action\u002Freducer ceremony.\n",[14,5663,5665],{"id":5664},"choosing-the-right-tool-decision-guide","Choosing the Right Tool: Decision Guide",[19,5667,5669],{"filename":5668,"language":22},"decision_guide.js",[24,5670,5672],{"className":26,"code":5671,"language":22,"meta":28,"style":28},"\u002F\u002F DECISION TREE:\n\n\u002F\u002F 1. Is the state SERVER data (fetched from an API, can go stale)?\n\u002F\u002F    → YES: React Query, SWR, or RTK Query. NOT Context\u002FRedux\u002FZustand\u002FJotai.\n\n\u002F\u002F 2. Is the state local to a single component?\n\u002F\u002F    → YES: useState or useReducer. No need for anything else.\n\n\u002F\u002F 3. Is the state shared between a few parent\u002Fchild components?\n\u002F\u002F    → YES: Lift state up to the common parent. Prop drilling is fine for 1-2 levels.\n\n\u002F\u002F 4. Is the state shared across many components at different tree depths?\n\u002F\u002F    → Does it change INFREQUENTLY (theme, locale, auth user)?\n\u002F\u002F      → YES: Context. Split into multiple Contexts if needed.\n\u002F\u002F    → Does it change FREQUENTLY (cart, notifications, form state)?\n\u002F\u002F      → Need structure + DevTools for a large team?\n\u002F\u002F        → YES: Redux Toolkit\n\u002F\u002F      → Want minimal ceremony, no Provider?\n\u002F\u002F        → YES: Zustand\n\u002F\u002F      → State is naturally many small independent\u002Fderived pieces?\n\u002F\u002F        → YES: Jotai\n\n\u002F\u002F 5. Do you need to read\u002Fupdate state OUTSIDE React components?\n\u002F\u002F    (WebSocket handlers, analytics, utility functions)\n\u002F\u002F    → Context can't do this → Zustand, Redux, or Jotai.\n\n\u002F\u002F 6. Do you need time-travel debugging and enforced action\u002Freducer structure?\n\u002F\u002F    → Redux Toolkit (the only one with first-class DevTools time-travel).\n\n\u002F\u002F SUMMARY TABLE:\n\u002F\u002F | Situation                                      | Use                    |\n\u002F\u002F |------------------------------------------------|------------------------|\n\u002F\u002F | Server data (API responses)                    | React Query \u002F SWR      |\n\u002F\u002F | Component-local state                          | useState \u002F useReducer  |\n\u002F\u002F | Lifted state (parent + 1-2 children)           | Props (no library)     |\n\u002F\u002F | Infrequently-changing app-wide state           | Context (split if needed) |\n\u002F\u002F | Frequently-changing global state, minimal fuss | Zustand                |\n\u002F\u002F | Large team, enforced structure, DevTools       | Redux Toolkit          |\n\u002F\u002F | Many small independent\u002Fderived pieces          | Jotai                  |\n\u002F\u002F | State needed outside React components          | Zustand \u002F Redux \u002F Jotai |\n",[30,5673,5674,5679,5683,5688,5693,5697,5702,5707,5711,5716,5721,5725,5730,5735,5740,5745,5750,5755,5760,5765,5770,5775,5779,5784,5789,5794,5798,5803,5808,5812,5817,5822,5827,5832,5837,5842,5847,5852,5857,5862],{"__ignoreMap":28},[33,5675,5676],{"class":35,"line":36},[33,5677,5678],{"class":39},"\u002F\u002F DECISION TREE:\n",[33,5680,5681],{"class":35,"line":43},[33,5682,77],{"emptyLinePlaceholder":76},[33,5684,5685],{"class":35,"line":49},[33,5686,5687],{"class":39},"\u002F\u002F 1. Is the state SERVER data (fetched from an API, can go stale)?\n",[33,5689,5690],{"class":35,"line":55},[33,5691,5692],{"class":39},"\u002F\u002F    → YES: React Query, SWR, or RTK Query. NOT Context\u002FRedux\u002FZustand\u002FJotai.\n",[33,5694,5695],{"class":35,"line":61},[33,5696,77],{"emptyLinePlaceholder":76},[33,5698,5699],{"class":35,"line":67},[33,5700,5701],{"class":39},"\u002F\u002F 2. Is the state local to a single component?\n",[33,5703,5704],{"class":35,"line":73},[33,5705,5706],{"class":39},"\u002F\u002F    → YES: useState or useReducer. No need for anything else.\n",[33,5708,5709],{"class":35,"line":80},[33,5710,77],{"emptyLinePlaceholder":76},[33,5712,5713],{"class":35,"line":108},[33,5714,5715],{"class":39},"\u002F\u002F 3. Is the state shared between a few parent\u002Fchild components?\n",[33,5717,5718],{"class":35,"line":113},[33,5719,5720],{"class":39},"\u002F\u002F    → YES: Lift state up to the common parent. Prop drilling is fine for 1-2 levels.\n",[33,5722,5723],{"class":35,"line":132},[33,5724,77],{"emptyLinePlaceholder":76},[33,5726,5727],{"class":35,"line":165},[33,5728,5729],{"class":39},"\u002F\u002F 4. Is the state shared across many components at different tree depths?\n",[33,5731,5732],{"class":35,"line":194},[33,5733,5734],{"class":39},"\u002F\u002F    → Does it change INFREQUENTLY (theme, locale, auth user)?\n",[33,5736,5737],{"class":35,"line":218},[33,5738,5739],{"class":39},"\u002F\u002F      → YES: Context. Split into multiple Contexts if needed.\n",[33,5741,5742],{"class":35,"line":223},[33,5743,5744],{"class":39},"\u002F\u002F    → Does it change FREQUENTLY (cart, notifications, form state)?\n",[33,5746,5747],{"class":35,"line":229},[33,5748,5749],{"class":39},"\u002F\u002F      → Need structure + DevTools for a large team?\n",[33,5751,5752],{"class":35,"line":235},[33,5753,5754],{"class":39},"\u002F\u002F        → YES: Redux Toolkit\n",[33,5756,5757],{"class":35,"line":248},[33,5758,5759],{"class":39},"\u002F\u002F      → Want minimal ceremony, no Provider?\n",[33,5761,5762],{"class":35,"line":272},[33,5763,5764],{"class":39},"\u002F\u002F        → YES: Zustand\n",[33,5766,5767],{"class":35,"line":278},[33,5768,5769],{"class":39},"\u002F\u002F      → State is naturally many small independent\u002Fderived pieces?\n",[33,5771,5772],{"class":35,"line":283},[33,5773,5774],{"class":39},"\u002F\u002F        → YES: Jotai\n",[33,5776,5777],{"class":35,"line":294},[33,5778,77],{"emptyLinePlaceholder":76},[33,5780,5781],{"class":35,"line":319},[33,5782,5783],{"class":39},"\u002F\u002F 5. Do you need to read\u002Fupdate state OUTSIDE React components?\n",[33,5785,5786],{"class":35,"line":325},[33,5787,5788],{"class":39},"\u002F\u002F    (WebSocket handlers, analytics, utility functions)\n",[33,5790,5791],{"class":35,"line":331},[33,5792,5793],{"class":39},"\u002F\u002F    → Context can't do this → Zustand, Redux, or Jotai.\n",[33,5795,5796],{"class":35,"line":337},[33,5797,77],{"emptyLinePlaceholder":76},[33,5799,5800],{"class":35,"line":397},[33,5801,5802],{"class":39},"\u002F\u002F 6. Do you need time-travel debugging and enforced action\u002Freducer structure?\n",[33,5804,5805],{"class":35,"line":961},[33,5806,5807],{"class":39},"\u002F\u002F    → Redux Toolkit (the only one with first-class DevTools time-travel).\n",[33,5809,5810],{"class":35,"line":967},[33,5811,77],{"emptyLinePlaceholder":76},[33,5813,5814],{"class":35,"line":977},[33,5815,5816],{"class":39},"\u002F\u002F SUMMARY TABLE:\n",[33,5818,5819],{"class":35,"line":992},[33,5820,5821],{"class":39},"\u002F\u002F | Situation                                      | Use                    |\n",[33,5823,5824],{"class":35,"line":1024},[33,5825,5826],{"class":39},"\u002F\u002F |------------------------------------------------|------------------------|\n",[33,5828,5829],{"class":35,"line":1393},[33,5830,5831],{"class":39},"\u002F\u002F | Server data (API responses)                    | React Query \u002F SWR      |\n",[33,5833,5834],{"class":35,"line":1399},[33,5835,5836],{"class":39},"\u002F\u002F | Component-local state                          | useState \u002F useReducer  |\n",[33,5838,5839],{"class":35,"line":1405},[33,5840,5841],{"class":39},"\u002F\u002F | Lifted state (parent + 1-2 children)           | Props (no library)     |\n",[33,5843,5844],{"class":35,"line":1420},[33,5845,5846],{"class":39},"\u002F\u002F | Infrequently-changing app-wide state           | Context (split if needed) |\n",[33,5848,5849],{"class":35,"line":1426},[33,5850,5851],{"class":39},"\u002F\u002F | Frequently-changing global state, minimal fuss | Zustand                |\n",[33,5853,5854],{"class":35,"line":1446},[33,5855,5856],{"class":39},"\u002F\u002F | Large team, enforced structure, DevTools       | Redux Toolkit          |\n",[33,5858,5859],{"class":35,"line":1451},[33,5860,5861],{"class":39},"\u002F\u002F | Many small independent\u002Fderived pieces          | Jotai                  |\n",[33,5863,5864],{"class":35,"line":1508},[33,5865,5866],{"class":39},"\u002F\u002F | State needed outside React components          | Zustand \u002F Redux \u002F Jotai |\n",[14,5868,5870],{"id":5869},"tips-tricks","💡 Tips & Tricks",[19,5872,5874],{"filename":5873,"language":22},"tips.js",[24,5875,5877],{"className":26,"code":5876,"language":22,"meta":28,"style":28},"\u002F\u002F [Idiom] Split one large Context into several narrowly-scoped ones BEFORE\n\u002F\u002F reaching for an external library — this alone fixes the \"every consumer\n\u002F\u002F re-renders on any change\" problem for many apps, at zero added dependencies.\n\n\u002F\u002F [Idiom] Keep server data (anything from an API) in React Query\u002FSWR and\n\u002F\u002F genuinely client-only state (modal open\u002Fclosed, form drafts, theme) in\n\u002F\u002F Context\u002FZustand\u002FJotai — mixing the two into one system reinvents a worse\n\u002F\u002F version of whichever data-fetching library you're avoiding.\n\n\u002F\u002F [Debug] In Zustand, ALWAYS pass a selector function\n\u002F\u002F (useCartStore(state => state.items.length)) rather than destructuring the\n\u002F\u002F whole store (const { items } = useCartStore()) — the latter subscribes the\n\u002F\u002F component to every field, reintroducing Context's coarse-re-render problem.\n\n\u002F\u002F [Idiom] Reach for Redux Toolkit specifically when a team needs enforced\n\u002F\u002F action\u002Freducer structure and time-travel debugging across a large codebase\n\u002F\u002F with many contributors — its ceremony is a FEATURE (consistency at scale),\n\u002F\u002F not merely legacy weight.\n\n\u002F\u002F [Idiom] Jotai's derived atoms are usually a cleaner solution than a useMemo\n\u002F\u002F reading from multiple Context values for cross-cutting computed state —\n\u002F\u002F consider it when an app's shared state is naturally composed of many small,\n\u002F\u002F independently-updated pieces rather than one large shape.\n\n\u002F\u002F [Performance] Split Context into a Value Context and a Setter Context when\n\u002F\u002F the value changes frequently but the setter never does — components that only\n\u002F\u002F dispatch (never read the current value) won't re-render on value changes.\n\n\u002F\u002F [Idiom] Use useSyncExternalStore (React 18+) for fine-grained Context\n\u002F\u002F subscriptions without a library — it's the same primitive Zustand\u002FJotai use\n\u002F\u002F internally, and lets consumers subscribe to only the slice they need.\n\n\u002F\u002F [Debug] Zustand's create() returning a hook means the store is a singleton\n\u002F\u002F across the whole app — unlike useState, all components calling useCartStore()\n\u002F\u002F share the exact same state. This is desired for global state but confusing if\n\u002F\u002F you expect per-component isolation.\n",[30,5878,5879,5884,5889,5894,5898,5903,5908,5913,5918,5922,5927,5932,5937,5942,5946,5951,5956,5961,5966,5970,5975,5980,5985,5990,5994,5999,6004,6009,6013,6018,6023,6028,6032,6037,6042,6047],{"__ignoreMap":28},[33,5880,5881],{"class":35,"line":36},[33,5882,5883],{"class":39},"\u002F\u002F [Idiom] Split one large Context into several narrowly-scoped ones BEFORE\n",[33,5885,5886],{"class":35,"line":43},[33,5887,5888],{"class":39},"\u002F\u002F reaching for an external library — this alone fixes the \"every consumer\n",[33,5890,5891],{"class":35,"line":49},[33,5892,5893],{"class":39},"\u002F\u002F re-renders on any change\" problem for many apps, at zero added dependencies.\n",[33,5895,5896],{"class":35,"line":55},[33,5897,77],{"emptyLinePlaceholder":76},[33,5899,5900],{"class":35,"line":61},[33,5901,5902],{"class":39},"\u002F\u002F [Idiom] Keep server data (anything from an API) in React Query\u002FSWR and\n",[33,5904,5905],{"class":35,"line":67},[33,5906,5907],{"class":39},"\u002F\u002F genuinely client-only state (modal open\u002Fclosed, form drafts, theme) in\n",[33,5909,5910],{"class":35,"line":73},[33,5911,5912],{"class":39},"\u002F\u002F Context\u002FZustand\u002FJotai — mixing the two into one system reinvents a worse\n",[33,5914,5915],{"class":35,"line":80},[33,5916,5917],{"class":39},"\u002F\u002F version of whichever data-fetching library you're avoiding.\n",[33,5919,5920],{"class":35,"line":108},[33,5921,77],{"emptyLinePlaceholder":76},[33,5923,5924],{"class":35,"line":113},[33,5925,5926],{"class":39},"\u002F\u002F [Debug] In Zustand, ALWAYS pass a selector function\n",[33,5928,5929],{"class":35,"line":132},[33,5930,5931],{"class":39},"\u002F\u002F (useCartStore(state => state.items.length)) rather than destructuring the\n",[33,5933,5934],{"class":35,"line":165},[33,5935,5936],{"class":39},"\u002F\u002F whole store (const { items } = useCartStore()) — the latter subscribes the\n",[33,5938,5939],{"class":35,"line":194},[33,5940,5941],{"class":39},"\u002F\u002F component to every field, reintroducing Context's coarse-re-render problem.\n",[33,5943,5944],{"class":35,"line":218},[33,5945,77],{"emptyLinePlaceholder":76},[33,5947,5948],{"class":35,"line":223},[33,5949,5950],{"class":39},"\u002F\u002F [Idiom] Reach for Redux Toolkit specifically when a team needs enforced\n",[33,5952,5953],{"class":35,"line":229},[33,5954,5955],{"class":39},"\u002F\u002F action\u002Freducer structure and time-travel debugging across a large codebase\n",[33,5957,5958],{"class":35,"line":235},[33,5959,5960],{"class":39},"\u002F\u002F with many contributors — its ceremony is a FEATURE (consistency at scale),\n",[33,5962,5963],{"class":35,"line":248},[33,5964,5965],{"class":39},"\u002F\u002F not merely legacy weight.\n",[33,5967,5968],{"class":35,"line":272},[33,5969,77],{"emptyLinePlaceholder":76},[33,5971,5972],{"class":35,"line":278},[33,5973,5974],{"class":39},"\u002F\u002F [Idiom] Jotai's derived atoms are usually a cleaner solution than a useMemo\n",[33,5976,5977],{"class":35,"line":283},[33,5978,5979],{"class":39},"\u002F\u002F reading from multiple Context values for cross-cutting computed state —\n",[33,5981,5982],{"class":35,"line":294},[33,5983,5984],{"class":39},"\u002F\u002F consider it when an app's shared state is naturally composed of many small,\n",[33,5986,5987],{"class":35,"line":319},[33,5988,5989],{"class":39},"\u002F\u002F independently-updated pieces rather than one large shape.\n",[33,5991,5992],{"class":35,"line":325},[33,5993,77],{"emptyLinePlaceholder":76},[33,5995,5996],{"class":35,"line":331},[33,5997,5998],{"class":39},"\u002F\u002F [Performance] Split Context into a Value Context and a Setter Context when\n",[33,6000,6001],{"class":35,"line":337},[33,6002,6003],{"class":39},"\u002F\u002F the value changes frequently but the setter never does — components that only\n",[33,6005,6006],{"class":35,"line":397},[33,6007,6008],{"class":39},"\u002F\u002F dispatch (never read the current value) won't re-render on value changes.\n",[33,6010,6011],{"class":35,"line":961},[33,6012,77],{"emptyLinePlaceholder":76},[33,6014,6015],{"class":35,"line":967},[33,6016,6017],{"class":39},"\u002F\u002F [Idiom] Use useSyncExternalStore (React 18+) for fine-grained Context\n",[33,6019,6020],{"class":35,"line":977},[33,6021,6022],{"class":39},"\u002F\u002F subscriptions without a library — it's the same primitive Zustand\u002FJotai use\n",[33,6024,6025],{"class":35,"line":992},[33,6026,6027],{"class":39},"\u002F\u002F internally, and lets consumers subscribe to only the slice they need.\n",[33,6029,6030],{"class":35,"line":1024},[33,6031,77],{"emptyLinePlaceholder":76},[33,6033,6034],{"class":35,"line":1393},[33,6035,6036],{"class":39},"\u002F\u002F [Debug] Zustand's create() returning a hook means the store is a singleton\n",[33,6038,6039],{"class":35,"line":1399},[33,6040,6041],{"class":39},"\u002F\u002F across the whole app — unlike useState, all components calling useCartStore()\n",[33,6043,6044],{"class":35,"line":1405},[33,6045,6046],{"class":39},"\u002F\u002F share the exact same state. This is desired for global state but confusing if\n",[33,6048,6049],{"class":35,"line":1420},[33,6050,6051],{"class":39},"\u002F\u002F you expect per-component isolation.\n",[14,6053,6055],{"id":6054},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[19,6057,6059],{"filename":6058,"language":22},"edge_cases.js",[24,6060,6062],{"className":26,"code":6061,"language":22,"meta":28,"style":28},"\u002F\u002F [Gotcha] A single Context bundling many unrelated fields re-renders EVERY\n\u002F\u002F consumer on ANY field's change, even ones a component never reads — the #1\n\u002F\u002F real-world Context performance complaint. Splitting into multiple Contexts\n\u002F\u002F (not necessarily abandoning Context) is usually the correct first fix.\n\n\u002F\u002F [Gotcha] Redux Toolkit's state.items.push(...)-looking reducer code is NOT\n\u002F\u002F actually a mutation — it works only because RTK wraps reducers in Immer, which\n\u002F\u002F intercepts the apparent mutation and produces an immutable update. Writing the\n\u002F\u002F same \"mutating\" code in a plain useState updater or outside an RTK reducer\n\u002F\u002F would be a real, broken mutation.\n\n\u002F\u002F [Gotcha] Zustand's create() returning a hook means the store is a true\n\u002F\u002F singleton across the whole app by default — all components calling\n\u002F\u002F useCartStore() share the exact same store. This is usually desired for global\n\u002F\u002F state but is a common point of confusion for developers expecting per-component\n\u002F\u002F isolation (like useState provides).\n\n\u002F\u002F [Gotcha] Context has NO way to read or update its value outside of a React\n\u002F\u002F component's render — Redux, Zustand, and Jotai all provide non-hook APIs\n\u002F\u002F (store.getState(), useCartStore.getState()) to close this gap. This matters\n\u002F\u002F for WebSocket handlers, analytics calls, or plain utility functions.\n\n\u002F\u002F [Gotcha] NONE of Context, Redux, Zustand, or Jotai include caching, refetching,\n\u002F\u002F or staleness logic for server data — using any of them to store API responses\n\u002F\u002F directly reproduces the race conditions and stale-data problems from chapter 17\n\u002F\u002F that data-fetching libraries specifically solve. Keep server state and client\n\u002F\u002F state in SEPARATE systems.\n\n\u002F\u002F [Gotcha] Context value object identity: if you pass a new object literal as\n\u002F\u002F the Provider value every render (value={{ a, b, c }}), EVERY consumer\n\u002F\u002F re-renders on EVERY Provider render, even if a, b, c haven't changed. Wrap\n\u002F\u002F the value in useMemo to prevent unnecessary re-renders.\n\n\u002F\u002F [Gotcha] Zustand selectors that return new object\u002Farray references every call\n\u002F\u002F (state => ({ items: state.items, count: state.items.length })) cause infinite\n\u002F\u002F re-render loops because the selector result is referentially new each time.\n\u002F\u002F Use useShallow (Zustand 4.4+) or return primitives, not new objects.\n\n\u002F\u002F [Gotcha] Jotai atoms are identified by their object reference, not by a string\n\u002F\u002F key — creating an atom inside a component body (const myAtom = atom(0)) makes\n\u002F\u002F a NEW atom every render, losing all state. Always create atoms at module scope\n\u002F\u002F or use atomFamily\u002FatomWithStorage for dynamic atoms.\n",[30,6063,6064,6069,6074,6079,6084,6088,6093,6098,6103,6108,6113,6117,6122,6127,6132,6137,6142,6146,6151,6156,6161,6166,6170,6175,6180,6185,6190,6195,6199,6204,6209,6214,6219,6223,6228,6233,6238,6243,6247,6252,6257,6262],{"__ignoreMap":28},[33,6065,6066],{"class":35,"line":36},[33,6067,6068],{"class":39},"\u002F\u002F [Gotcha] A single Context bundling many unrelated fields re-renders EVERY\n",[33,6070,6071],{"class":35,"line":43},[33,6072,6073],{"class":39},"\u002F\u002F consumer on ANY field's change, even ones a component never reads — the #1\n",[33,6075,6076],{"class":35,"line":49},[33,6077,6078],{"class":39},"\u002F\u002F real-world Context performance complaint. Splitting into multiple Contexts\n",[33,6080,6081],{"class":35,"line":55},[33,6082,6083],{"class":39},"\u002F\u002F (not necessarily abandoning Context) is usually the correct first fix.\n",[33,6085,6086],{"class":35,"line":61},[33,6087,77],{"emptyLinePlaceholder":76},[33,6089,6090],{"class":35,"line":67},[33,6091,6092],{"class":39},"\u002F\u002F [Gotcha] Redux Toolkit's state.items.push(...)-looking reducer code is NOT\n",[33,6094,6095],{"class":35,"line":73},[33,6096,6097],{"class":39},"\u002F\u002F actually a mutation — it works only because RTK wraps reducers in Immer, which\n",[33,6099,6100],{"class":35,"line":80},[33,6101,6102],{"class":39},"\u002F\u002F intercepts the apparent mutation and produces an immutable update. Writing the\n",[33,6104,6105],{"class":35,"line":108},[33,6106,6107],{"class":39},"\u002F\u002F same \"mutating\" code in a plain useState updater or outside an RTK reducer\n",[33,6109,6110],{"class":35,"line":113},[33,6111,6112],{"class":39},"\u002F\u002F would be a real, broken mutation.\n",[33,6114,6115],{"class":35,"line":132},[33,6116,77],{"emptyLinePlaceholder":76},[33,6118,6119],{"class":35,"line":165},[33,6120,6121],{"class":39},"\u002F\u002F [Gotcha] Zustand's create() returning a hook means the store is a true\n",[33,6123,6124],{"class":35,"line":194},[33,6125,6126],{"class":39},"\u002F\u002F singleton across the whole app by default — all components calling\n",[33,6128,6129],{"class":35,"line":218},[33,6130,6131],{"class":39},"\u002F\u002F useCartStore() share the exact same store. This is usually desired for global\n",[33,6133,6134],{"class":35,"line":223},[33,6135,6136],{"class":39},"\u002F\u002F state but is a common point of confusion for developers expecting per-component\n",[33,6138,6139],{"class":35,"line":229},[33,6140,6141],{"class":39},"\u002F\u002F isolation (like useState provides).\n",[33,6143,6144],{"class":35,"line":235},[33,6145,77],{"emptyLinePlaceholder":76},[33,6147,6148],{"class":35,"line":248},[33,6149,6150],{"class":39},"\u002F\u002F [Gotcha] Context has NO way to read or update its value outside of a React\n",[33,6152,6153],{"class":35,"line":272},[33,6154,6155],{"class":39},"\u002F\u002F component's render — Redux, Zustand, and Jotai all provide non-hook APIs\n",[33,6157,6158],{"class":35,"line":278},[33,6159,6160],{"class":39},"\u002F\u002F (store.getState(), useCartStore.getState()) to close this gap. This matters\n",[33,6162,6163],{"class":35,"line":283},[33,6164,6165],{"class":39},"\u002F\u002F for WebSocket handlers, analytics calls, or plain utility functions.\n",[33,6167,6168],{"class":35,"line":294},[33,6169,77],{"emptyLinePlaceholder":76},[33,6171,6172],{"class":35,"line":319},[33,6173,6174],{"class":39},"\u002F\u002F [Gotcha] NONE of Context, Redux, Zustand, or Jotai include caching, refetching,\n",[33,6176,6177],{"class":35,"line":325},[33,6178,6179],{"class":39},"\u002F\u002F or staleness logic for server data — using any of them to store API responses\n",[33,6181,6182],{"class":35,"line":331},[33,6183,6184],{"class":39},"\u002F\u002F directly reproduces the race conditions and stale-data problems from chapter 17\n",[33,6186,6187],{"class":35,"line":337},[33,6188,6189],{"class":39},"\u002F\u002F that data-fetching libraries specifically solve. Keep server state and client\n",[33,6191,6192],{"class":35,"line":397},[33,6193,6194],{"class":39},"\u002F\u002F state in SEPARATE systems.\n",[33,6196,6197],{"class":35,"line":961},[33,6198,77],{"emptyLinePlaceholder":76},[33,6200,6201],{"class":35,"line":967},[33,6202,6203],{"class":39},"\u002F\u002F [Gotcha] Context value object identity: if you pass a new object literal as\n",[33,6205,6206],{"class":35,"line":977},[33,6207,6208],{"class":39},"\u002F\u002F the Provider value every render (value={{ a, b, c }}), EVERY consumer\n",[33,6210,6211],{"class":35,"line":992},[33,6212,6213],{"class":39},"\u002F\u002F re-renders on EVERY Provider render, even if a, b, c haven't changed. Wrap\n",[33,6215,6216],{"class":35,"line":1024},[33,6217,6218],{"class":39},"\u002F\u002F the value in useMemo to prevent unnecessary re-renders.\n",[33,6220,6221],{"class":35,"line":1393},[33,6222,77],{"emptyLinePlaceholder":76},[33,6224,6225],{"class":35,"line":1399},[33,6226,6227],{"class":39},"\u002F\u002F [Gotcha] Zustand selectors that return new object\u002Farray references every call\n",[33,6229,6230],{"class":35,"line":1405},[33,6231,6232],{"class":39},"\u002F\u002F (state => ({ items: state.items, count: state.items.length })) cause infinite\n",[33,6234,6235],{"class":35,"line":1420},[33,6236,6237],{"class":39},"\u002F\u002F re-render loops because the selector result is referentially new each time.\n",[33,6239,6240],{"class":35,"line":1426},[33,6241,6242],{"class":39},"\u002F\u002F Use useShallow (Zustand 4.4+) or return primitives, not new objects.\n",[33,6244,6245],{"class":35,"line":1446},[33,6246,77],{"emptyLinePlaceholder":76},[33,6248,6249],{"class":35,"line":1451},[33,6250,6251],{"class":39},"\u002F\u002F [Gotcha] Jotai atoms are identified by their object reference, not by a string\n",[33,6253,6254],{"class":35,"line":1508},[33,6255,6256],{"class":39},"\u002F\u002F key — creating an atom inside a component body (const myAtom = atom(0)) makes\n",[33,6258,6259],{"class":35,"line":3050},[33,6260,6261],{"class":39},"\u002F\u002F a NEW atom every render, losing all state. Always create atoms at module scope\n",[33,6263,6264],{"class":35,"line":3057},[33,6265,6266],{"class":39},"\u002F\u002F or use atomFamily\u002FatomWithStorage for dynamic atoms.\n",[14,6268,6270],{"id":6269},"spot-the-bug","🧠 Spot the Bug",[3650,6272,6273,6274,6277],{},"A settings page's ",[30,6275,6276],{},"ThemeToggle"," button is reported as \"laggy\" — clicking it takes a visibly long moment to respond, even though toggling a theme should be instantaneous:",[19,6279,6281],{"filename":6280,"language":22},"spot_the_bug.js",[24,6282,6284],{"className":26,"code":6283,"language":22,"meta":28,"style":28},"const AppContext = createContext(null)\n\nfunction AppProvider({ children }) {\n  const [theme, setTheme] = useState('light')\n  const [searchResults, setSearchResults] = useState([])\n\n  useEffect(() => {\n    const id = setInterval(() => {\n      fetchLiveSearchResults().then(setSearchResults)\n    }, 200)\n    return () => clearInterval(id)\n  }, [])\n\n  return (\n    \u003CAppContext.Provider value={{ theme, setTheme, searchResults }}>\n      {children}\n    \u003C\u002FAppContext.Provider>\n  )\n}\n\nfunction ThemeToggle() {\n  const { theme, setTheme } = useContext(AppContext)\n  return (\n    \u003Cbutton onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>\n      {theme === 'light' ? '🌙' : '☀️'}\n    \u003C\u002Fbutton>\n  )\n}\n",[30,6285,6286,6302,6306,6318,6342,6364,6368,6378,6397,6410,6420,6435,6440,6444,6450,6463,6467,6475,6479,6483,6487,6495,6515,6521,6559,6580,6588,6592],{"__ignoreMap":28},[33,6287,6288,6290,6292,6294,6296,6298,6300],{"class":35,"line":36},[33,6289,84],{"class":83},[33,6291,88],{"class":87},[33,6293,91],{"class":83},[33,6295,95],{"class":94},[33,6297,99],{"class":98},[33,6299,102],{"class":87},[33,6301,105],{"class":98},[33,6303,6304],{"class":35,"line":43},[33,6305,77],{"emptyLinePlaceholder":76},[33,6307,6308,6310,6312,6314,6316],{"class":35,"line":49},[33,6309,116],{"class":83},[33,6311,119],{"class":94},[33,6313,122],{"class":98},[33,6315,126],{"class":125},[33,6317,129],{"class":98},[33,6319,6320,6322,6324,6326,6328,6330,6332,6334,6336,6338,6340],{"class":35,"line":55},[33,6321,135],{"class":83},[33,6323,138],{"class":98},[33,6325,172],{"class":87},[33,6327,144],{"class":98},[33,6329,177],{"class":87},[33,6331,150],{"class":98},[33,6333,153],{"class":83},[33,6335,156],{"class":94},[33,6337,99],{"class":98},[33,6339,189],{"class":188},[33,6341,105],{"class":98},[33,6343,6344,6346,6348,6351,6353,6356,6358,6360,6362],{"class":35,"line":61},[33,6345,135],{"class":83},[33,6347,138],{"class":98},[33,6349,6350],{"class":87},"searchResults",[33,6352,144],{"class":98},[33,6354,6355],{"class":87},"setSearchResults",[33,6357,150],{"class":98},[33,6359,153],{"class":83},[33,6361,156],{"class":94},[33,6363,215],{"class":98},[33,6365,6366],{"class":35,"line":67},[33,6367,77],{"emptyLinePlaceholder":76},[33,6369,6370,6372,6374,6376],{"class":35,"line":73},[33,6371,3539],{"class":94},[33,6373,765],{"class":98},[33,6375,356],{"class":83},[33,6377,1128],{"class":98},[33,6379,6380,6383,6386,6388,6391,6393,6395],{"class":35,"line":80},[33,6381,6382],{"class":83},"    const",[33,6384,6385],{"class":87}," id",[33,6387,91],{"class":83},[33,6389,6390],{"class":94}," setInterval",[33,6392,765],{"class":98},[33,6394,356],{"class":83},[33,6396,1128],{"class":98},[33,6398,6399,6402,6404,6407],{"class":35,"line":108},[33,6400,6401],{"class":94},"      fetchLiveSearchResults",[33,6403,2161],{"class":98},[33,6405,6406],{"class":94},"then",[33,6408,6409],{"class":98},"(setSearchResults)\n",[33,6411,6412,6415,6418],{"class":35,"line":113},[33,6413,6414],{"class":98},"    }, ",[33,6416,6417],{"class":87},"200",[33,6419,105],{"class":98},[33,6421,6422,6425,6427,6429,6432],{"class":35,"line":132},[33,6423,6424],{"class":83},"    return",[33,6426,1251],{"class":98},[33,6428,356],{"class":83},[33,6430,6431],{"class":94}," clearInterval",[33,6433,6434],{"class":98},"(id)\n",[33,6436,6437],{"class":35,"line":165},[33,6438,6439],{"class":98},"  }, [])\n",[33,6441,6442],{"class":35,"line":194},[33,6443,77],{"emptyLinePlaceholder":76},[33,6445,6446,6448],{"class":35,"line":218},[33,6447,251],{"class":83},[33,6449,505],{"class":98},[33,6451,6452,6454,6456,6458,6460],{"class":35,"line":223},[33,6453,510],{"class":98},[33,6455,257],{"class":87},[33,6457,240],{"class":94},[33,6459,153],{"class":83},[33,6461,6462],{"class":98},"{{ theme, setTheme, searchResults }}>\n",[33,6464,6465],{"class":35,"line":229},[33,6466,5249],{"class":98},[33,6468,6469,6471,6473],{"class":35,"line":235},[33,6470,599],{"class":98},[33,6472,257],{"class":87},[33,6474,269],{"class":98},[33,6476,6477],{"class":35,"line":248},[33,6478,608],{"class":98},[33,6480,6481],{"class":35,"line":272},[33,6482,275],{"class":98},[33,6484,6485],{"class":35,"line":278},[33,6486,77],{"emptyLinePlaceholder":76},[33,6488,6489,6491,6493],{"class":35,"line":283},[33,6490,116],{"class":83},[33,6492,288],{"class":94},[33,6494,291],{"class":98},[33,6496,6497,6499,6501,6503,6505,6507,6509,6511,6513],{"class":35,"line":294},[33,6498,135],{"class":83},[33,6500,299],{"class":98},[33,6502,172],{"class":87},[33,6504,144],{"class":98},[33,6506,177],{"class":87},[33,6508,308],{"class":98},[33,6510,153],{"class":83},[33,6512,313],{"class":94},[33,6514,316],{"class":98},[33,6516,6517,6519],{"class":35,"line":319},[33,6518,251],{"class":83},[33,6520,505],{"class":98},[33,6522,6523,6525,6527,6529,6531,6533,6535,6537,6539,6541,6543,6545,6547,6549,6551,6553,6555,6557],{"class":35,"line":325},[33,6524,510],{"class":98},[33,6526,345],{"class":344},[33,6528,348],{"class":94},[33,6530,153],{"class":83},[33,6532,353],{"class":98},[33,6534,356],{"class":83},[33,6536,359],{"class":94},[33,6538,99],{"class":98},[33,6540,364],{"class":125},[33,6542,367],{"class":83},[33,6544,370],{"class":98},[33,6546,373],{"class":83},[33,6548,376],{"class":188},[33,6550,379],{"class":83},[33,6552,382],{"class":188},[33,6554,385],{"class":83},[33,6556,376],{"class":188},[33,6558,530],{"class":98},[33,6560,6561,6564,6566,6568,6570,6573,6575,6578],{"class":35,"line":331},[33,6562,6563],{"class":98},"      {theme ",[33,6565,373],{"class":83},[33,6567,376],{"class":188},[33,6569,379],{"class":83},[33,6571,6572],{"class":188}," '🌙'",[33,6574,385],{"class":83},[33,6576,6577],{"class":188}," '☀️'",[33,6579,275],{"class":98},[33,6581,6582,6584,6586],{"class":35,"line":337},[33,6583,599],{"class":98},[33,6585,345],{"class":344},[33,6587,269],{"class":98},[33,6589,6590],{"class":35,"line":397},[33,6591,608],{"class":98},[33,6593,6594],{"class":35,"line":961},[33,6595,275],{"class":98},[6597,6598,6599,6603,6633,6649,7014],"details",{},[6600,6601,6602],"summary",{},"Answer",[3650,6604,6605,6607,6608,6612,6613,6615,6616,6619,6620,6622,6623,6625,6626,6628,6629,6632],{},[30,6606,6350],{}," updates every 200ms from a polling effect, and it lives in the ",[6609,6610,6611],"strong",{},"same Context value object"," as ",[30,6614,172],{},". Every 200ms, the Provider re-renders with a new ",[30,6617,6618],{},"value"," object (a new object reference every time, even if ",[30,6621,172],{}," itself hasn't changed), and because Context has no field-level subscription granularity, ",[30,6624,6276],{}," — which reads only ",[30,6627,172],{}," — re-renders every single time too, ",[6609,6630,6631],{},"5 times a second",". The perceived \"lag\" on click is really the button competing with constant background re-render churn.",[3650,6634,6635,6638,6639,6641,6642,6645,6646,6648],{},[6609,6636,6637],{},"Fix",": split the fast-changing ",[30,6640,6350],{}," into a separate Context (or move it to Zustand\u002FReact Query), and ",[30,6643,6644],{},"useMemo"," the ",[30,6647,172],{}," Context's value:",[24,6650,6652],{"className":26,"code":6651,"language":22,"meta":28,"style":28},"const ThemeContext = createContext(null)\nconst SearchContext = createContext(null)\n\nfunction AppProvider({ children }) {\n  const [theme, setTheme] = useState('light')\n  const [searchResults, setSearchResults] = useState([])\n\n  useEffect(() => {\n    const id = setInterval(() => {\n      fetchLiveSearchResults().then(setSearchResults)\n    }, 200)\n    return () => clearInterval(id)\n  }, [])\n\n  const themeValue = useMemo(() => ({ theme, setTheme }), [theme, setTheme])\n\n  return (\n    \u003CThemeContext.Provider value={themeValue}>\n      \u003CSearchContext.Provider value={searchResults}>\n        {children}\n      \u003C\u002FSearchContext.Provider>\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\nfunction ThemeToggle() {\n  const { theme, setTheme } = useContext(ThemeContext)\n  \u002F\u002F Now only re-renders when theme changes — not on every searchResults update.\n  return (\n    \u003Cbutton onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>\n      {theme === 'light' ? '🌙' : '☀️'}\n    \u003C\u002Fbutton>\n  )\n}\n",[30,6653,6654,6670,6687,6691,6703,6727,6747,6751,6761,6777,6787,6795,6807,6811,6815,6834,6838,6844,6857,6871,6875,6883,6891,6895,6899,6903,6911,6931,6936,6942,6980,6998,7006,7010],{"__ignoreMap":28},[33,6655,6656,6658,6660,6662,6664,6666,6668],{"class":35,"line":36},[33,6657,84],{"class":83},[33,6659,438],{"class":87},[33,6661,91],{"class":83},[33,6663,95],{"class":94},[33,6665,99],{"class":98},[33,6667,102],{"class":87},[33,6669,105],{"class":98},[33,6671,6672,6674,6677,6679,6681,6683,6685],{"class":35,"line":43},[33,6673,84],{"class":83},[33,6675,6676],{"class":87}," SearchContext",[33,6678,91],{"class":83},[33,6680,95],{"class":94},[33,6682,99],{"class":98},[33,6684,102],{"class":87},[33,6686,105],{"class":98},[33,6688,6689],{"class":35,"line":49},[33,6690,77],{"emptyLinePlaceholder":76},[33,6692,6693,6695,6697,6699,6701],{"class":35,"line":55},[33,6694,116],{"class":83},[33,6696,119],{"class":94},[33,6698,122],{"class":98},[33,6700,126],{"class":125},[33,6702,129],{"class":98},[33,6704,6705,6707,6709,6711,6713,6715,6717,6719,6721,6723,6725],{"class":35,"line":61},[33,6706,135],{"class":83},[33,6708,138],{"class":98},[33,6710,172],{"class":87},[33,6712,144],{"class":98},[33,6714,177],{"class":87},[33,6716,150],{"class":98},[33,6718,153],{"class":83},[33,6720,156],{"class":94},[33,6722,99],{"class":98},[33,6724,189],{"class":188},[33,6726,105],{"class":98},[33,6728,6729,6731,6733,6735,6737,6739,6741,6743,6745],{"class":35,"line":67},[33,6730,135],{"class":83},[33,6732,138],{"class":98},[33,6734,6350],{"class":87},[33,6736,144],{"class":98},[33,6738,6355],{"class":87},[33,6740,150],{"class":98},[33,6742,153],{"class":83},[33,6744,156],{"class":94},[33,6746,215],{"class":98},[33,6748,6749],{"class":35,"line":73},[33,6750,77],{"emptyLinePlaceholder":76},[33,6752,6753,6755,6757,6759],{"class":35,"line":80},[33,6754,3539],{"class":94},[33,6756,765],{"class":98},[33,6758,356],{"class":83},[33,6760,1128],{"class":98},[33,6762,6763,6765,6767,6769,6771,6773,6775],{"class":35,"line":108},[33,6764,6382],{"class":83},[33,6766,6385],{"class":87},[33,6768,91],{"class":83},[33,6770,6390],{"class":94},[33,6772,765],{"class":98},[33,6774,356],{"class":83},[33,6776,1128],{"class":98},[33,6778,6779,6781,6783,6785],{"class":35,"line":113},[33,6780,6401],{"class":94},[33,6782,2161],{"class":98},[33,6784,6406],{"class":94},[33,6786,6409],{"class":98},[33,6788,6789,6791,6793],{"class":35,"line":132},[33,6790,6414],{"class":98},[33,6792,6417],{"class":87},[33,6794,105],{"class":98},[33,6796,6797,6799,6801,6803,6805],{"class":35,"line":165},[33,6798,6424],{"class":83},[33,6800,1251],{"class":98},[33,6802,356],{"class":83},[33,6804,6431],{"class":94},[33,6806,6434],{"class":98},[33,6808,6809],{"class":35,"line":194},[33,6810,6439],{"class":98},[33,6812,6813],{"class":35,"line":218},[33,6814,77],{"emptyLinePlaceholder":76},[33,6816,6817,6819,6822,6824,6827,6829,6831],{"class":35,"line":223},[33,6818,135],{"class":83},[33,6820,6821],{"class":87}," themeValue",[33,6823,91],{"class":83},[33,6825,6826],{"class":94}," useMemo",[33,6828,765],{"class":98},[33,6830,356],{"class":83},[33,6832,6833],{"class":98}," ({ theme, setTheme }), [theme, setTheme])\n",[33,6835,6836],{"class":35,"line":229},[33,6837,77],{"emptyLinePlaceholder":76},[33,6839,6840,6842],{"class":35,"line":235},[33,6841,251],{"class":83},[33,6843,505],{"class":98},[33,6845,6846,6848,6850,6852,6854],{"class":35,"line":248},[33,6847,510],{"class":98},[33,6849,513],{"class":87},[33,6851,240],{"class":94},[33,6853,153],{"class":83},[33,6855,6856],{"class":98},"{themeValue}>\n",[33,6858,6859,6861,6864,6866,6868],{"class":35,"line":272},[33,6860,535],{"class":98},[33,6862,6863],{"class":87},"SearchContext.Provider",[33,6865,240],{"class":94},[33,6867,153],{"class":83},[33,6869,6870],{"class":98},"{searchResults}>\n",[33,6872,6873],{"class":35,"line":278},[33,6874,860],{"class":98},[33,6876,6877,6879,6881],{"class":35,"line":283},[33,6878,590],{"class":98},[33,6880,6863],{"class":87},[33,6882,269],{"class":98},[33,6884,6885,6887,6889],{"class":35,"line":294},[33,6886,599],{"class":98},[33,6888,513],{"class":87},[33,6890,269],{"class":98},[33,6892,6893],{"class":35,"line":319},[33,6894,608],{"class":98},[33,6896,6897],{"class":35,"line":325},[33,6898,275],{"class":98},[33,6900,6901],{"class":35,"line":331},[33,6902,77],{"emptyLinePlaceholder":76},[33,6904,6905,6907,6909],{"class":35,"line":337},[33,6906,116],{"class":83},[33,6908,288],{"class":94},[33,6910,291],{"class":98},[33,6912,6913,6915,6917,6919,6921,6923,6925,6927,6929],{"class":35,"line":397},[33,6914,135],{"class":83},[33,6916,299],{"class":98},[33,6918,172],{"class":87},[33,6920,144],{"class":98},[33,6922,177],{"class":87},[33,6924,308],{"class":98},[33,6926,153],{"class":83},[33,6928,313],{"class":94},[33,6930,645],{"class":98},[33,6932,6933],{"class":35,"line":961},[33,6934,6935],{"class":39},"  \u002F\u002F Now only re-renders when theme changes — not on every searchResults update.\n",[33,6937,6938,6940],{"class":35,"line":967},[33,6939,251],{"class":83},[33,6941,505],{"class":98},[33,6943,6944,6946,6948,6950,6952,6954,6956,6958,6960,6962,6964,6966,6968,6970,6972,6974,6976,6978],{"class":35,"line":977},[33,6945,510],{"class":98},[33,6947,345],{"class":344},[33,6949,348],{"class":94},[33,6951,153],{"class":83},[33,6953,353],{"class":98},[33,6955,356],{"class":83},[33,6957,359],{"class":94},[33,6959,99],{"class":98},[33,6961,364],{"class":125},[33,6963,367],{"class":83},[33,6965,370],{"class":98},[33,6967,373],{"class":83},[33,6969,376],{"class":188},[33,6971,379],{"class":83},[33,6973,382],{"class":188},[33,6975,385],{"class":83},[33,6977,376],{"class":188},[33,6979,530],{"class":98},[33,6981,6982,6984,6986,6988,6990,6992,6994,6996],{"class":35,"line":992},[33,6983,6563],{"class":98},[33,6985,373],{"class":83},[33,6987,376],{"class":188},[33,6989,379],{"class":83},[33,6991,6572],{"class":188},[33,6993,385],{"class":83},[33,6995,6577],{"class":188},[33,6997,275],{"class":98},[33,6999,7000,7002,7004],{"class":35,"line":1024},[33,7001,599],{"class":98},[33,7003,345],{"class":344},[33,7005,269],{"class":98},[33,7007,7008],{"class":35,"line":1393},[33,7009,608],{"class":98},[33,7011,7012],{"class":35,"line":1399},[33,7013,275],{"class":98},[3650,7015,7016,7017,7019,7020,7022],{},"The lesson: state that changes at very different frequencies (a slow-changing ",[30,7018,172],{}," and a fast-polling ",[30,7021,6350],{},") should never share one Context value — split them so components that only care about the slow-changing field don't re-render on every fast-changing update.",[14,7024,7026],{"id":7025},"key-takeaways","Key Takeaways",[19,7028,7030],{"filename":7029,"language":22},"key_takeaways.js",[24,7031,7033],{"className":26,"code":7032,"language":22,"meta":28,"style":28},"\u002F\u002F 1. Context's two structural limits: coarse-grained re-rendering (every consumer\n\u002F\u002F    re-renders on any value change) and no built-in way to read\u002Fupdate state\n\u002F\u002F    outside React components. Both are fine at small scale, limiting at large.\n\n\u002F\u002F 2. Splitting one large Context into several narrowly-scoped ones (or splitting\n\u002F\u002F    value+setter into separate Contexts) is often sufficient — try it BEFORE\n\u002F\u002F    adopting an external library.\n\n\u002F\u002F 3. useSyncExternalStore (React 18+) provides fine-grained subscriptions without\n\u002F\u002F    a library — the same primitive Zustand\u002FJotai use internally.\n\n\u002F\u002F 4. Zustand: global state with minimal ceremony, no Provider, access from outside\n\u002F\u002F    React. Always use selector functions — destructuring the whole store\n\u002F\u002F    reintroduces Context's coarse-re-render problem.\n\n\u002F\u002F 5. Redux Toolkit: enforced structure (actions\u002Freducers), fine-grained useSelector\n\u002F\u002F    subscriptions, mature DevTools with time-travel, and RTK Query for server\n\u002F\u002F    state. More ceremony than smaller apps need, but valuable for large teams.\n\n\u002F\u002F 6. Jotai: composes state from small, independent (and derivable) atoms —\n\u002F\u002F    fine-grained subscriptions by construction. Derived atoms replace useMemo\n\u002F\u002F    for cross-cutting computed state. Create atoms at module scope, never in\n\u002F\u002F    component body.\n\n\u002F\u002F 7. SERVER STATE ≠ CLIENT STATE. Server data (API responses, can go stale, needs\n\u002F\u002F    caching\u002Frefetching\u002Finvalidation) belongs in React Query\u002FSWR\u002FRTK Query.\n\u002F\u002F    Client state (UI, forms, preferences) belongs in Context\u002FZustand\u002FRedux\u002FJotai.\n\u002F\u002F    Never mix the two — storing API responses in a state library reinvents a\n\u002F\u002F    worse version of a data-fetching library.\n\n\u002F\u002F 8. Decision tree: server data → React Query\u002FSWR; local → useState; lifted →\n\u002F\u002F    props; infreq-changing shared → Context; freq-changing shared + minimal →\n\u002F\u002F    Zustand; large team + structure → Redux Toolkit; many small pieces → Jotai;\n\u002F\u002F    state outside React → Zustand\u002FRedux\u002FJotai (not Context).\n",[30,7034,7035,7040,7045,7050,7054,7059,7064,7069,7073,7078,7083,7087,7092,7097,7102,7106,7111,7116,7121,7125,7130,7135,7140,7145,7149,7154,7159,7164,7169,7174,7178,7183,7188,7193],{"__ignoreMap":28},[33,7036,7037],{"class":35,"line":36},[33,7038,7039],{"class":39},"\u002F\u002F 1. Context's two structural limits: coarse-grained re-rendering (every consumer\n",[33,7041,7042],{"class":35,"line":43},[33,7043,7044],{"class":39},"\u002F\u002F    re-renders on any value change) and no built-in way to read\u002Fupdate state\n",[33,7046,7047],{"class":35,"line":49},[33,7048,7049],{"class":39},"\u002F\u002F    outside React components. Both are fine at small scale, limiting at large.\n",[33,7051,7052],{"class":35,"line":55},[33,7053,77],{"emptyLinePlaceholder":76},[33,7055,7056],{"class":35,"line":61},[33,7057,7058],{"class":39},"\u002F\u002F 2. Splitting one large Context into several narrowly-scoped ones (or splitting\n",[33,7060,7061],{"class":35,"line":67},[33,7062,7063],{"class":39},"\u002F\u002F    value+setter into separate Contexts) is often sufficient — try it BEFORE\n",[33,7065,7066],{"class":35,"line":73},[33,7067,7068],{"class":39},"\u002F\u002F    adopting an external library.\n",[33,7070,7071],{"class":35,"line":80},[33,7072,77],{"emptyLinePlaceholder":76},[33,7074,7075],{"class":35,"line":108},[33,7076,7077],{"class":39},"\u002F\u002F 3. useSyncExternalStore (React 18+) provides fine-grained subscriptions without\n",[33,7079,7080],{"class":35,"line":113},[33,7081,7082],{"class":39},"\u002F\u002F    a library — the same primitive Zustand\u002FJotai use internally.\n",[33,7084,7085],{"class":35,"line":132},[33,7086,77],{"emptyLinePlaceholder":76},[33,7088,7089],{"class":35,"line":165},[33,7090,7091],{"class":39},"\u002F\u002F 4. Zustand: global state with minimal ceremony, no Provider, access from outside\n",[33,7093,7094],{"class":35,"line":194},[33,7095,7096],{"class":39},"\u002F\u002F    React. Always use selector functions — destructuring the whole store\n",[33,7098,7099],{"class":35,"line":218},[33,7100,7101],{"class":39},"\u002F\u002F    reintroduces Context's coarse-re-render problem.\n",[33,7103,7104],{"class":35,"line":223},[33,7105,77],{"emptyLinePlaceholder":76},[33,7107,7108],{"class":35,"line":229},[33,7109,7110],{"class":39},"\u002F\u002F 5. Redux Toolkit: enforced structure (actions\u002Freducers), fine-grained useSelector\n",[33,7112,7113],{"class":35,"line":235},[33,7114,7115],{"class":39},"\u002F\u002F    subscriptions, mature DevTools with time-travel, and RTK Query for server\n",[33,7117,7118],{"class":35,"line":248},[33,7119,7120],{"class":39},"\u002F\u002F    state. More ceremony than smaller apps need, but valuable for large teams.\n",[33,7122,7123],{"class":35,"line":272},[33,7124,77],{"emptyLinePlaceholder":76},[33,7126,7127],{"class":35,"line":278},[33,7128,7129],{"class":39},"\u002F\u002F 6. Jotai: composes state from small, independent (and derivable) atoms —\n",[33,7131,7132],{"class":35,"line":283},[33,7133,7134],{"class":39},"\u002F\u002F    fine-grained subscriptions by construction. Derived atoms replace useMemo\n",[33,7136,7137],{"class":35,"line":294},[33,7138,7139],{"class":39},"\u002F\u002F    for cross-cutting computed state. Create atoms at module scope, never in\n",[33,7141,7142],{"class":35,"line":319},[33,7143,7144],{"class":39},"\u002F\u002F    component body.\n",[33,7146,7147],{"class":35,"line":325},[33,7148,77],{"emptyLinePlaceholder":76},[33,7150,7151],{"class":35,"line":331},[33,7152,7153],{"class":39},"\u002F\u002F 7. SERVER STATE ≠ CLIENT STATE. Server data (API responses, can go stale, needs\n",[33,7155,7156],{"class":35,"line":337},[33,7157,7158],{"class":39},"\u002F\u002F    caching\u002Frefetching\u002Finvalidation) belongs in React Query\u002FSWR\u002FRTK Query.\n",[33,7160,7161],{"class":35,"line":397},[33,7162,7163],{"class":39},"\u002F\u002F    Client state (UI, forms, preferences) belongs in Context\u002FZustand\u002FRedux\u002FJotai.\n",[33,7165,7166],{"class":35,"line":961},[33,7167,7168],{"class":39},"\u002F\u002F    Never mix the two — storing API responses in a state library reinvents a\n",[33,7170,7171],{"class":35,"line":967},[33,7172,7173],{"class":39},"\u002F\u002F    worse version of a data-fetching library.\n",[33,7175,7176],{"class":35,"line":977},[33,7177,77],{"emptyLinePlaceholder":76},[33,7179,7180],{"class":35,"line":992},[33,7181,7182],{"class":39},"\u002F\u002F 8. Decision tree: server data → React Query\u002FSWR; local → useState; lifted →\n",[33,7184,7185],{"class":35,"line":1024},[33,7186,7187],{"class":39},"\u002F\u002F    props; infreq-changing shared → Context; freq-changing shared + minimal →\n",[33,7189,7190],{"class":35,"line":1393},[33,7191,7192],{"class":39},"\u002F\u002F    Zustand; large team + structure → Redux Toolkit; many small pieces → Jotai;\n",[33,7194,7195],{"class":35,"line":1399},[33,7196,7197],{"class":39},"\u002F\u002F    state outside React → Zustand\u002FRedux\u002FJotai (not Context).\n",[7199,7200,7201],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}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 .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}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);}",{"title":28,"searchDepth":43,"depth":43,"links":7203},[7204,7209,7210,7214,7218,7221,7222,7223,7224,7225,7226,7227,7228],{"id":16,"depth":43,"text":17,"children":7205},[7206,7207,7208],{"id":403,"depth":49,"text":404},{"id":706,"depth":49,"text":707},{"id":1029,"depth":49,"text":1030},{"id":1513,"depth":43,"text":1514},{"id":1719,"depth":43,"text":1720,"children":7211},[7212,7213],{"id":2190,"depth":49,"text":2191},{"id":2489,"depth":49,"text":2490},{"id":2710,"depth":43,"text":2711,"children":7215},[7216,7217],{"id":3134,"depth":49,"text":3135},{"id":3681,"depth":49,"text":3682},{"id":4088,"depth":43,"text":4089,"children":7219},[7220],{"id":4442,"depth":49,"text":4443},{"id":4762,"depth":43,"text":4763},{"id":4854,"depth":43,"text":4855},{"id":5109,"depth":43,"text":5110},{"id":5664,"depth":43,"text":5665},{"id":5869,"depth":43,"text":5870},{"id":6054,"depth":43,"text":6055},{"id":6269,"depth":43,"text":6270},{"id":7025,"depth":43,"text":7026},"When Context is enough and when it strains, comparison of Zustand\u002FRedux Toolkit\u002FJotai, server state vs client state, concrete migration from Context to Zustand, Redux Toolkit basic example, Jotai atomic state, and choosing the right tool. Code-first reference for mid-to-senior React engineers.","md",{},"\u002Freact\u002F18-context-and-state-management-libraries",{"title":5,"description":7229},"react\u002F18-context-and-state-management-libraries","ijgRiDs_7IL5rL-opV0zPjq8k30yCmunqdNtjGFkjtQ",1789924652446]