[{"data":1,"prerenderedAt":1384},["ShallowReactive",2],{"blog-\u002Fblog\u002Freact-context-vs-redux-guide":3},{"id":4,"title":5,"body":6,"description":1369,"difficulty":1370,"extension":1371,"framework":1372,"frameworkSlug":1373,"meta":1374,"navigation":107,"order":111,"path":1375,"qaPath":1376,"seo":1377,"stem":1378,"subtopic":1379,"topic":1380,"topicSlug":1381,"updated":1382,"__hash__":1383},"blog\u002Fblog\u002Freact-context-vs-redux-guide.md","React Context vs Redux — Complete Interview Guide",{"type":7,"value":8,"toc":1356},"minimark",[9,14,18,26,30,38,58,290,297,303,307,310,321,458,465,527,554,560,609,619,622,626,629,634,641,647,759,765,769,772,775,778,791,891,894,898,904,1205,1215,1218,1221,1225,1228,1233,1253,1258,1275,1280,1298,1305,1309,1316,1319,1349,1352],[10,11,13],"h2",{"id":12},"the-question-every-react-interviewer-asks","The question every React interviewer asks",[15,16,17],"p",{},"\"When would you use Context instead of Redux?\" is one of those interview\nquestions that reveals more about a candidate than almost any other. A junior\nanswer is \"Context is simpler.\" A mid-level answer names some trade-offs.\nA senior answer starts with re-render behaviour, works through middleware and\nselector granularity, and ends with a concrete decision framework.",[15,19,20,21,25],{},"This guide gives you the full picture — not just the surface-level talking\npoints, but the mechanics that explain ",[22,23,24],"em",{},"why"," the trade-offs exist. By the end\nyou'll be able to answer the question from first principles, which is exactly\nwhat experienced interviewers are listening for.",[10,27,29],{"id":28},"what-context-api-actually-is-and-isnt","What Context API Actually Is (and Isn't)",[15,31,32,33,37],{},"The Context API is ",[34,35,36],"strong",{},"a dependency injection mechanism, not a state manager",".\nThat distinction matters.",[15,39,40,41,45,46,49,50,53,54,57],{},"When you write ",[42,43,44],"code",{},"createContext"," and wrap a subtree in a ",[42,47,48],{},"Provider",", you're\ncreating a channel through which a value can be read by any descendant without\nthreading it through props. You still manage the state yourself — with\n",[42,51,52],{},"useState",", ",[42,55,56],{},"useReducer",", or anything else — and Context simply makes it\navailable.",[59,60,65],"pre",{"className":61,"code":62,"language":63,"meta":64,"style":64},"language-jsx shiki shiki-themes github-light github-dark","const ThemeContext = createContext('light')   \u002F\u002F 'light' is the default (no Provider)\n\nfunction App() {\n  const [theme, setTheme] = useState('dark')\n  return (\n    \u003CThemeContext.Provider value={theme}>\n      \u003CLayout \u002F>\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\nfunction Button() {\n  const theme = useContext(ThemeContext)   \u002F\u002F 'dark' — no props needed\n  return \u003Cbutton className={`btn btn--${theme}`}>Click\u003C\u002Fbutton>\n}\n","jsx","",[42,66,67,102,109,121,155,164,181,193,204,210,216,221,231,250,285],{"__ignoreMap":64},[68,69,72,76,80,83,87,91,95,98],"span",{"class":70,"line":71},"line",1,[68,73,75],{"class":74},"szBVR","const",[68,77,79],{"class":78},"sj4cs"," ThemeContext",[68,81,82],{"class":74}," =",[68,84,86],{"class":85},"sScJk"," createContext",[68,88,90],{"class":89},"sVt8B","(",[68,92,94],{"class":93},"sZZnC","'light'",[68,96,97],{"class":89},")   ",[68,99,101],{"class":100},"sJ8bj","\u002F\u002F 'light' is the default (no Provider)\n",[68,103,105],{"class":70,"line":104},2,[68,106,108],{"emptyLinePlaceholder":107},true,"\n",[68,110,112,115,118],{"class":70,"line":111},3,[68,113,114],{"class":74},"function",[68,116,117],{"class":85}," App",[68,119,120],{"class":89},"() {\n",[68,122,124,127,130,133,135,138,141,144,147,149,152],{"class":70,"line":123},4,[68,125,126],{"class":74},"  const",[68,128,129],{"class":89}," [",[68,131,132],{"class":78},"theme",[68,134,53],{"class":89},[68,136,137],{"class":78},"setTheme",[68,139,140],{"class":89},"] ",[68,142,143],{"class":74},"=",[68,145,146],{"class":85}," useState",[68,148,90],{"class":89},[68,150,151],{"class":93},"'dark'",[68,153,154],{"class":89},")\n",[68,156,158,161],{"class":70,"line":157},5,[68,159,160],{"class":74},"  return",[68,162,163],{"class":89}," (\n",[68,165,167,170,173,176,178],{"class":70,"line":166},6,[68,168,169],{"class":89},"    \u003C",[68,171,172],{"class":78},"ThemeContext.Provider",[68,174,175],{"class":85}," value",[68,177,143],{"class":74},[68,179,180],{"class":89},"{theme}>\n",[68,182,184,187,190],{"class":70,"line":183},7,[68,185,186],{"class":89},"      \u003C",[68,188,189],{"class":78},"Layout",[68,191,192],{"class":89}," \u002F>\n",[68,194,196,199,201],{"class":70,"line":195},8,[68,197,198],{"class":89},"    \u003C\u002F",[68,200,172],{"class":78},[68,202,203],{"class":89},">\n",[68,205,207],{"class":70,"line":206},9,[68,208,209],{"class":89},"  )\n",[68,211,213],{"class":70,"line":212},10,[68,214,215],{"class":89},"}\n",[68,217,219],{"class":70,"line":218},11,[68,220,108],{"emptyLinePlaceholder":107},[68,222,224,226,229],{"class":70,"line":223},12,[68,225,114],{"class":74},[68,227,228],{"class":85}," Button",[68,230,120],{"class":89},[68,232,234,236,239,241,244,247],{"class":70,"line":233},13,[68,235,126],{"class":74},[68,237,238],{"class":78}," theme",[68,240,82],{"class":74},[68,242,243],{"class":85}," useContext",[68,245,246],{"class":89},"(ThemeContext)   ",[68,248,249],{"class":100},"\u002F\u002F 'dark' — no props needed\n",[68,251,253,255,258,262,265,267,270,273,275,278,281,283],{"class":70,"line":252},14,[68,254,160],{"class":74},[68,256,257],{"class":89}," \u003C",[68,259,261],{"class":260},"s9eBZ","button",[68,263,264],{"class":85}," className",[68,266,143],{"class":74},[68,268,269],{"class":89},"{",[68,271,272],{"class":93},"`btn btn--${",[68,274,132],{"class":89},[68,276,277],{"class":93},"}`",[68,279,280],{"class":89},"}>Click\u003C\u002F",[68,282,261],{"class":260},[68,284,203],{"class":89},[68,286,288],{"class":70,"line":287},15,[68,289,215],{"class":89},[15,291,292,293,296],{},"This is genuinely elegant for ",[34,294,295],{},"ambient data"," that the whole app needs but\nthat changes rarely: the authenticated user object, the active locale, the\ncolour theme, feature flags loaded at startup. These values are read in dozens\nof places but typically update only once or twice per session — at login, on a\ntheme toggle, on a language switch.",[15,298,299,300,302],{},"The mental model: Context is a conduit. The state still lives in a\n",[42,301,52],{}," call somewhere; Context just makes it accessible at a distance.",[10,304,306],{"id":305},"the-re-render-problem-why-context-doesnt-scale-for-frequent-updates","The Re-render Problem — Why Context Doesn't Scale for Frequent Updates",[15,308,309],{},"Here is the behaviour that trips up most candidates when they first encounter\nit.",[15,311,312,313,316,317,320],{},"Every component that calls ",[42,314,315],{},"useContext(MyContext)"," re-renders when the\nProvider's ",[42,318,319],{},"value"," prop changes — and \"changes\" means the reference is\ndifferent, not just the content. React does not diff the previous and next\ncontext values at a field level. It compares references.",[59,322,324],{"className":61,"code":323,"language":63,"meta":64,"style":64},"function AppProvider({ children }) {\n  const [user, setUser] = useState(null)\n  const [cart, setCart] = useState([])\n\n  \u002F\u002F ❌ A new object is created on every render of AppProvider.\n  \u002F\u002F Adding one item to the cart re-renders EVERY component that\n  \u002F\u002F calls useContext(AppContext), including UserBadge, HeaderNav,\n  \u002F\u002F SettingsPanel — anything that subscribes to this context.\n  return (\n    \u003CAppContext.Provider value={{ user, setUser, cart, setCart }}>\n      {children}\n    \u003C\u002FAppContext.Provider>\n  )\n}\n",[42,325,326,343,370,393,397,402,407,412,417,423,437,442,450,454],{"__ignoreMap":64},[68,327,328,330,333,336,340],{"class":70,"line":71},[68,329,114],{"class":74},[68,331,332],{"class":85}," AppProvider",[68,334,335],{"class":89},"({ ",[68,337,339],{"class":338},"s4XuR","children",[68,341,342],{"class":89}," }) {\n",[68,344,345,347,349,352,354,357,359,361,363,365,368],{"class":70,"line":104},[68,346,126],{"class":74},[68,348,129],{"class":89},[68,350,351],{"class":78},"user",[68,353,53],{"class":89},[68,355,356],{"class":78},"setUser",[68,358,140],{"class":89},[68,360,143],{"class":74},[68,362,146],{"class":85},[68,364,90],{"class":89},[68,366,367],{"class":78},"null",[68,369,154],{"class":89},[68,371,372,374,376,379,381,384,386,388,390],{"class":70,"line":111},[68,373,126],{"class":74},[68,375,129],{"class":89},[68,377,378],{"class":78},"cart",[68,380,53],{"class":89},[68,382,383],{"class":78},"setCart",[68,385,140],{"class":89},[68,387,143],{"class":74},[68,389,146],{"class":85},[68,391,392],{"class":89},"([])\n",[68,394,395],{"class":70,"line":123},[68,396,108],{"emptyLinePlaceholder":107},[68,398,399],{"class":70,"line":157},[68,400,401],{"class":100},"  \u002F\u002F ❌ A new object is created on every render of AppProvider.\n",[68,403,404],{"class":70,"line":166},[68,405,406],{"class":100},"  \u002F\u002F Adding one item to the cart re-renders EVERY component that\n",[68,408,409],{"class":70,"line":183},[68,410,411],{"class":100},"  \u002F\u002F calls useContext(AppContext), including UserBadge, HeaderNav,\n",[68,413,414],{"class":70,"line":195},[68,415,416],{"class":100},"  \u002F\u002F SettingsPanel — anything that subscribes to this context.\n",[68,418,419,421],{"class":70,"line":206},[68,420,160],{"class":74},[68,422,163],{"class":89},[68,424,425,427,430,432,434],{"class":70,"line":212},[68,426,169],{"class":89},[68,428,429],{"class":78},"AppContext.Provider",[68,431,175],{"class":85},[68,433,143],{"class":74},[68,435,436],{"class":89},"{{ user, setUser, cart, setCart }}>\n",[68,438,439],{"class":70,"line":218},[68,440,441],{"class":89},"      {children}\n",[68,443,444,446,448],{"class":70,"line":223},[68,445,198],{"class":89},[68,447,429],{"class":78},[68,449,203],{"class":89},[68,451,452],{"class":70,"line":233},[68,453,209],{"class":89},[68,455,456],{"class":70,"line":252},[68,457,215],{"class":89},[15,459,460,461,464],{},"You can partially address this with ",[42,462,463],{},"useMemo",":",[59,466,468],{"className":61,"code":467,"language":63,"meta":64,"style":64},"const value = useMemo(\n  () => ({ user, setUser, cart, setCart }),\n  [user, cart]   \u002F\u002F setUser \u002F setCart are stable references from useState\n)\nreturn \u003CAppContext.Provider value={value}>{children}\u003C\u002FAppContext.Provider>\n",[42,469,470,484,495,503,507],{"__ignoreMap":64},[68,471,472,474,476,478,481],{"class":70,"line":71},[68,473,75],{"class":74},[68,475,175],{"class":78},[68,477,82],{"class":74},[68,479,480],{"class":85}," useMemo",[68,482,483],{"class":89},"(\n",[68,485,486,489,492],{"class":70,"line":104},[68,487,488],{"class":89},"  () ",[68,490,491],{"class":74},"=>",[68,493,494],{"class":89}," ({ user, setUser, cart, setCart }),\n",[68,496,497,500],{"class":70,"line":111},[68,498,499],{"class":89},"  [user, cart]   ",[68,501,502],{"class":100},"\u002F\u002F setUser \u002F setCart are stable references from useState\n",[68,504,505],{"class":70,"line":123},[68,506,154],{"class":89},[68,508,509,512,514,516,518,520,523,525],{"class":70,"line":157},[68,510,511],{"class":74},"return",[68,513,257],{"class":89},[68,515,429],{"class":78},[68,517,175],{"class":85},[68,519,143],{"class":74},[68,521,522],{"class":89},"{value}>{children}\u003C\u002F",[68,524,429],{"class":78},[68,526,203],{"class":89},[15,528,529,530,532,533,535,536,538,539,547,548,550,551,553],{},"Now ",[42,531,319],{}," only gets a new reference when ",[42,534,351],{}," or ",[42,537,378],{}," actually changes.\nBut here's the crucial limitation: ",[34,540,541,542,535,544,546],{},"every subscriber still re-renders whenever\n",[42,543,351],{},[42,545,378],{}," changes",", even components that only need ",[42,549,351],{}," and have no\nbusiness re-rendering because ",[42,552,378],{}," changed.",[15,555,556,557,464],{},"Redux solves this with ",[34,558,559],{},"selectors",[59,561,565],{"className":562,"code":563,"language":564,"meta":64,"style":64},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F With Redux — only re-renders when state.auth.user changes\nconst user = useSelector(state => state.auth.user)\n\n\u002F\u002F Adding an item to the cart dispatches a different slice;\n\u002F\u002F this component does not re-render at all\n","js",[42,566,567,572,595,599,604],{"__ignoreMap":64},[68,568,569],{"class":70,"line":71},[68,570,571],{"class":100},"\u002F\u002F With Redux — only re-renders when state.auth.user changes\n",[68,573,574,576,579,581,584,586,589,592],{"class":70,"line":104},[68,575,75],{"class":74},[68,577,578],{"class":78}," user",[68,580,82],{"class":74},[68,582,583],{"class":85}," useSelector",[68,585,90],{"class":89},[68,587,588],{"class":338},"state",[68,590,591],{"class":74}," =>",[68,593,594],{"class":89}," state.auth.user)\n",[68,596,597],{"class":70,"line":111},[68,598,108],{"emptyLinePlaceholder":107},[68,600,601],{"class":70,"line":123},[68,602,603],{"class":100},"\u002F\u002F Adding an item to the cart dispatches a different slice;\n",[68,605,606],{"class":70,"line":157},[68,607,608],{"class":100},"\u002F\u002F this component does not re-render at all\n",[15,610,611,614,615,618],{},[42,612,613],{},"useSelector"," compares the selector's return value between renders (using\nstrict equality by default). If the result hasn't changed, the component is\nskipped. This is ",[34,616,617],{},"subscription granularity"," — and Context has no native\nequivalent.",[15,620,621],{},"In practice this means: use Context for values that update infrequently, split\nyour contexts so each has one reason to change, and switch to Redux (or Zustand)\nonce updates become frequent or consumers multiply.",[10,623,625],{"id":624},"what-redux-adds-middleware-devtools-selectors","What Redux Adds — Middleware, DevTools, Selectors",[15,627,628],{},"Redux's value over Context comes from three capabilities that Context simply\ncannot replicate.",[630,631,633],"h3",{"id":632},"middleware","Middleware",[15,635,636,637,640],{},"The Redux middleware pipeline sits between ",[42,638,639],{},"dispatch"," and the reducer. Every\naction flows through it, giving you a place to intercept, transform, delay, or\nfork into side effects.",[15,642,643,646],{},[42,644,645],{},"redux-thunk"," (built into Redux Toolkit) allows action creators to be async\nfunctions:",[59,648,650],{"className":562,"code":649,"language":564,"meta":64,"style":64},"\u002F\u002F Without thunk you can only dispatch plain objects.\n\u002F\u002F With thunk you dispatch a function that receives dispatch and getState.\nexport const fetchUser = createAsyncThunk('users\u002Ffetch', async (id) => {\n  const response = await api.getUser(id)\n  return response.data\n})\n\n\u002F\u002F RTK's createAsyncThunk automatically dispatches:\n\u002F\u002F   users\u002Ffetch\u002Fpending   → set loading: true\n\u002F\u002F   users\u002Ffetch\u002Ffulfilled → set data, loading: false\n\u002F\u002F   users\u002Ffetch\u002Frejected  → set error, loading: false\n",[42,651,652,657,662,702,723,730,735,739,744,749,754],{"__ignoreMap":64},[68,653,654],{"class":70,"line":71},[68,655,656],{"class":100},"\u002F\u002F Without thunk you can only dispatch plain objects.\n",[68,658,659],{"class":70,"line":104},[68,660,661],{"class":100},"\u002F\u002F With thunk you dispatch a function that receives dispatch and getState.\n",[68,663,664,667,670,673,675,678,680,683,685,688,691,694,697,699],{"class":70,"line":111},[68,665,666],{"class":74},"export",[68,668,669],{"class":74}," const",[68,671,672],{"class":78}," fetchUser",[68,674,82],{"class":74},[68,676,677],{"class":85}," createAsyncThunk",[68,679,90],{"class":89},[68,681,682],{"class":93},"'users\u002Ffetch'",[68,684,53],{"class":89},[68,686,687],{"class":74},"async",[68,689,690],{"class":89}," (",[68,692,693],{"class":338},"id",[68,695,696],{"class":89},") ",[68,698,491],{"class":74},[68,700,701],{"class":89}," {\n",[68,703,704,706,709,711,714,717,720],{"class":70,"line":123},[68,705,126],{"class":74},[68,707,708],{"class":78}," response",[68,710,82],{"class":74},[68,712,713],{"class":74}," await",[68,715,716],{"class":89}," api.",[68,718,719],{"class":85},"getUser",[68,721,722],{"class":89},"(id)\n",[68,724,725,727],{"class":70,"line":157},[68,726,160],{"class":74},[68,728,729],{"class":89}," response.data\n",[68,731,732],{"class":70,"line":166},[68,733,734],{"class":89},"})\n",[68,736,737],{"class":70,"line":183},[68,738,108],{"emptyLinePlaceholder":107},[68,740,741],{"class":70,"line":195},[68,742,743],{"class":100},"\u002F\u002F RTK's createAsyncThunk automatically dispatches:\n",[68,745,746],{"class":70,"line":206},[68,747,748],{"class":100},"\u002F\u002F   users\u002Ffetch\u002Fpending   → set loading: true\n",[68,750,751],{"class":70,"line":212},[68,752,753],{"class":100},"\u002F\u002F   users\u002Ffetch\u002Ffulfilled → set data, loading: false\n",[68,755,756],{"class":70,"line":218},[68,757,758],{"class":100},"\u002F\u002F   users\u002Ffetch\u002Frejected  → set error, loading: false\n",[15,760,761,764],{},[42,762,763],{},"redux-saga"," goes further — generators let you model complex async flows like\n\"take the latest search request, cancel the previous one, retry on failure.\"\nNone of this is available in Context. You can write async functions inside a\nContext Provider, but you lose the action log, DevTools visibility, and the\nability to compose or cancel flows at the middleware layer.",[630,766,768],{"id":767},"devtools","DevTools",[15,770,771],{},"Redux DevTools is genuinely transformative for debugging. You get a full,\nordered log of every dispatched action, a diff of what changed in the store\nfor each one, and time-travel: click any past action to jump the UI back to\nthat exact state.",[15,773,774],{},"Redux Toolkit wires this up automatically in development mode — no\nconfiguration needed. Context has no equivalent. The closest you can get is\nReact DevTools component inspection, which shows you current state but gives\nyou no history, no diffs, and no replay.",[630,776,777],{"id":559},"Selectors",[15,779,780,782,783,786,787,790],{},[42,781,613],{}," from ",[42,784,785],{},"react-redux"," lets you derive and subscribe to a specific\nslice of the store. Reselect's ",[42,788,789],{},"createSelector"," adds memoisation — the derived\nvalue is only recomputed when its inputs change:",[59,792,794],{"className":562,"code":793,"language":564,"meta":64,"style":64},"const selectVisibleTodos = createSelector(\n  [state => state.todos, state => state.filter],\n  (todos, filter) => todos.filter(t => matchesFilter(t, filter))\n)\n\n\u002F\u002F Component re-renders only when the filtered result actually changes\nconst visibleTodos = useSelector(selectVisibleTodos)\n",[42,795,796,810,829,864,868,872,877],{"__ignoreMap":64},[68,797,798,800,803,805,808],{"class":70,"line":71},[68,799,75],{"class":74},[68,801,802],{"class":78}," selectVisibleTodos",[68,804,82],{"class":74},[68,806,807],{"class":85}," createSelector",[68,809,483],{"class":89},[68,811,812,815,817,819,822,824,826],{"class":70,"line":104},[68,813,814],{"class":89},"  [",[68,816,588],{"class":338},[68,818,591],{"class":74},[68,820,821],{"class":89}," state.todos, ",[68,823,588],{"class":338},[68,825,591],{"class":74},[68,827,828],{"class":89}," state.filter],\n",[68,830,831,834,837,839,842,844,846,849,851,853,856,858,861],{"class":70,"line":111},[68,832,833],{"class":89},"  (",[68,835,836],{"class":338},"todos",[68,838,53],{"class":89},[68,840,841],{"class":338},"filter",[68,843,696],{"class":89},[68,845,491],{"class":74},[68,847,848],{"class":89}," todos.",[68,850,841],{"class":85},[68,852,90],{"class":89},[68,854,855],{"class":338},"t",[68,857,591],{"class":74},[68,859,860],{"class":85}," matchesFilter",[68,862,863],{"class":89},"(t, filter))\n",[68,865,866],{"class":70,"line":123},[68,867,154],{"class":89},[68,869,870],{"class":70,"line":157},[68,871,108],{"emptyLinePlaceholder":107},[68,873,874],{"class":70,"line":166},[68,875,876],{"class":100},"\u002F\u002F Component re-renders only when the filtered result actually changes\n",[68,878,879,881,884,886,888],{"class":70,"line":183},[68,880,75],{"class":74},[68,882,883],{"class":78}," visibleTodos",[68,885,82],{"class":74},[68,887,583],{"class":85},[68,889,890],{"class":89},"(selectVisibleTodos)\n",[15,892,893],{},"This kind of computed, memoised, fine-grained subscription is not available in\nContext without third-party libraries.",[10,895,897],{"id":896},"context-usereducer-the-lightweight-middle-ground","Context + useReducer — The Lightweight Middle Ground",[15,899,900,901,903],{},"Before reaching for Redux, there is an intermediate option that works well for\nsmall-to-medium apps: pairing ",[42,902,56],{}," with Context.",[59,905,907],{"className":61,"code":906,"language":63,"meta":64,"style":64},"const CounterStateContext    = createContext(null)\nconst CounterDispatchContext = createContext(null)  \u002F\u002F split so dispatch-only consumers don't re-render\n\nfunction counterReducer(state, action) {\n  switch (action.type) {\n    case 'increment': return { count: state.count + 1 }\n    case 'decrement': return { count: state.count - 1 }\n    case 'reset':     return { count: 0 }\n    default:          throw new Error(`Unknown: ${action.type}`)\n  }\n}\n\nexport function CounterProvider({ children }) {\n  const [state, dispatch] = useReducer(counterReducer, { count: 0 })\n  return (\n    \u003CCounterStateContext.Provider value={state}>\n      \u003CCounterDispatchContext.Provider value={dispatch}>\n        {children}\n      \u003C\u002FCounterDispatchContext.Provider>\n    \u003C\u002FCounterStateContext.Provider>\n  )\n}\n",[42,908,909,927,948,952,971,979,1004,1024,1044,1078,1083,1087,1091,1107,1134,1140,1155,1170,1176,1186,1195,1200],{"__ignoreMap":64},[68,910,911,913,916,919,921,923,925],{"class":70,"line":71},[68,912,75],{"class":74},[68,914,915],{"class":78}," CounterStateContext",[68,917,918],{"class":74},"    =",[68,920,86],{"class":85},[68,922,90],{"class":89},[68,924,367],{"class":78},[68,926,154],{"class":89},[68,928,929,931,934,936,938,940,942,945],{"class":70,"line":104},[68,930,75],{"class":74},[68,932,933],{"class":78}," CounterDispatchContext",[68,935,82],{"class":74},[68,937,86],{"class":85},[68,939,90],{"class":89},[68,941,367],{"class":78},[68,943,944],{"class":89},")  ",[68,946,947],{"class":100},"\u002F\u002F split so dispatch-only consumers don't re-render\n",[68,949,950],{"class":70,"line":111},[68,951,108],{"emptyLinePlaceholder":107},[68,953,954,956,959,961,963,965,968],{"class":70,"line":123},[68,955,114],{"class":74},[68,957,958],{"class":85}," counterReducer",[68,960,90],{"class":89},[68,962,588],{"class":338},[68,964,53],{"class":89},[68,966,967],{"class":338},"action",[68,969,970],{"class":89},") {\n",[68,972,973,976],{"class":70,"line":157},[68,974,975],{"class":74},"  switch",[68,977,978],{"class":89}," (action.type) {\n",[68,980,981,984,987,990,992,995,998,1001],{"class":70,"line":166},[68,982,983],{"class":74},"    case",[68,985,986],{"class":93}," 'increment'",[68,988,989],{"class":89},": ",[68,991,511],{"class":74},[68,993,994],{"class":89}," { count: state.count ",[68,996,997],{"class":74},"+",[68,999,1000],{"class":78}," 1",[68,1002,1003],{"class":89}," }\n",[68,1005,1006,1008,1011,1013,1015,1017,1020,1022],{"class":70,"line":183},[68,1007,983],{"class":74},[68,1009,1010],{"class":93}," 'decrement'",[68,1012,989],{"class":89},[68,1014,511],{"class":74},[68,1016,994],{"class":89},[68,1018,1019],{"class":74},"-",[68,1021,1000],{"class":78},[68,1023,1003],{"class":89},[68,1025,1026,1028,1031,1034,1036,1039,1042],{"class":70,"line":195},[68,1027,983],{"class":74},[68,1029,1030],{"class":93}," 'reset'",[68,1032,1033],{"class":89},":     ",[68,1035,511],{"class":74},[68,1037,1038],{"class":89}," { count: ",[68,1040,1041],{"class":78},"0",[68,1043,1003],{"class":89},[68,1045,1046,1049,1052,1055,1058,1061,1063,1066,1068,1071,1074,1076],{"class":70,"line":206},[68,1047,1048],{"class":74},"    default",[68,1050,1051],{"class":89},":          ",[68,1053,1054],{"class":74},"throw",[68,1056,1057],{"class":74}," new",[68,1059,1060],{"class":85}," Error",[68,1062,90],{"class":89},[68,1064,1065],{"class":93},"`Unknown: ${",[68,1067,967],{"class":89},[68,1069,1070],{"class":93},".",[68,1072,1073],{"class":89},"type",[68,1075,277],{"class":93},[68,1077,154],{"class":89},[68,1079,1080],{"class":70,"line":212},[68,1081,1082],{"class":89},"  }\n",[68,1084,1085],{"class":70,"line":218},[68,1086,215],{"class":89},[68,1088,1089],{"class":70,"line":223},[68,1090,108],{"emptyLinePlaceholder":107},[68,1092,1093,1095,1098,1101,1103,1105],{"class":70,"line":233},[68,1094,666],{"class":74},[68,1096,1097],{"class":74}," function",[68,1099,1100],{"class":85}," CounterProvider",[68,1102,335],{"class":89},[68,1104,339],{"class":338},[68,1106,342],{"class":89},[68,1108,1109,1111,1113,1115,1117,1119,1121,1123,1126,1129,1131],{"class":70,"line":252},[68,1110,126],{"class":74},[68,1112,129],{"class":89},[68,1114,588],{"class":78},[68,1116,53],{"class":89},[68,1118,639],{"class":78},[68,1120,140],{"class":89},[68,1122,143],{"class":74},[68,1124,1125],{"class":85}," useReducer",[68,1127,1128],{"class":89},"(counterReducer, { count: ",[68,1130,1041],{"class":78},[68,1132,1133],{"class":89}," })\n",[68,1135,1136,1138],{"class":70,"line":287},[68,1137,160],{"class":74},[68,1139,163],{"class":89},[68,1141,1143,1145,1148,1150,1152],{"class":70,"line":1142},16,[68,1144,169],{"class":89},[68,1146,1147],{"class":78},"CounterStateContext.Provider",[68,1149,175],{"class":85},[68,1151,143],{"class":74},[68,1153,1154],{"class":89},"{state}>\n",[68,1156,1158,1160,1163,1165,1167],{"class":70,"line":1157},17,[68,1159,186],{"class":89},[68,1161,1162],{"class":78},"CounterDispatchContext.Provider",[68,1164,175],{"class":85},[68,1166,143],{"class":74},[68,1168,1169],{"class":89},"{dispatch}>\n",[68,1171,1173],{"class":70,"line":1172},18,[68,1174,1175],{"class":89},"        {children}\n",[68,1177,1179,1182,1184],{"class":70,"line":1178},19,[68,1180,1181],{"class":89},"      \u003C\u002F",[68,1183,1162],{"class":78},[68,1185,203],{"class":89},[68,1187,1189,1191,1193],{"class":70,"line":1188},20,[68,1190,198],{"class":89},[68,1192,1147],{"class":78},[68,1194,203],{"class":89},[68,1196,1198],{"class":70,"line":1197},21,[68,1199,209],{"class":89},[68,1201,1203],{"class":70,"line":1202},22,[68,1204,215],{"class":89},[15,1206,1207,1208,1211,1212,1214],{},"The key insight: dispatch is a ",[34,1209,1210],{},"stable reference"," (like ",[42,1213,52],{},"'s setter),\nso putting it in its own context means components that only dispatch will never\nre-render due to state changes.",[15,1216,1217],{},"This pattern gives you the same conceptual separation Redux offers — actions,\nreducer, centralised state — without the dependency. What it still lacks:\nmiddleware, DevTools, and selector granularity. Once your app needs any of\nthose three things, the incremental complexity of RTK is justified.",[15,1219,1220],{},"A good rule of thumb: Context + useReducer scales comfortably up to about five\nindependent pieces of complex state. Beyond that, the manual wiring required\nmatches or exceeds Redux Toolkit's setup cost.",[10,1222,1224],{"id":1223},"decision-framework-when-to-pick-each","Decision Framework — When to Pick Each",[15,1226,1227],{},"The right tool depends on three axes: how often the state updates, how complex\nthe async logic is, and how large the team is.",[15,1229,1230],{},[34,1231,1232],{},"Use Context when:",[1234,1235,1236,1240,1247,1250],"ul",{},[1237,1238,1239],"li",{},"The state changes infrequently — theme, auth user, locale, feature flags.",[1237,1241,1242,1243,1246],{},"No middleware is needed; async logic is a single ",[42,1244,1245],{},"async\u002Fawait"," call.",[1237,1248,1249],{},"The app is small or the state is scoped to a subtree.",[1237,1251,1252],{},"You want zero external dependencies.",[15,1254,1255],{},[34,1256,1257],{},"Use Redux Toolkit when:",[1234,1259,1260,1263,1266,1269,1272],{},[1237,1261,1262],{},"State updates frequently (cart updates, real-time feeds, form state with\nmany interdependent fields).",[1237,1264,1265],{},"You need async coordination: loading states, cancellation, polling, retries.",[1237,1267,1268],{},"Different parts of the store need to derive values from each other (selectors\nthat combine slices).",[1237,1270,1271],{},"Multiple engineers work on the codebase and enforced conventions reduce\naccidental coupling.",[1237,1273,1274],{},"Time-travel debugging or an action audit trail has real value.",[15,1276,1277],{},[34,1278,1279],{},"Use Zustand or Jotai when:",[1234,1281,1282,1285,1288,1295],{},[1237,1283,1284],{},"You need granular subscriptions but Redux feels like overkill.",[1237,1286,1287],{},"You want to avoid the Provider wrapping ceremony.",[1237,1289,1290,1291,1294],{},"Zustand: a single ",[42,1292,1293],{},"create"," call gives you a store with selectors, no\nProvider needed.",[1237,1296,1297],{},"Jotai: atomic model — each atom is independent, components subscribe to only\nthe atoms they read.",[15,1299,1300,1301,1304],{},"One thing to get right: ",[34,1302,1303],{},"server state is not the same as client state",".\nLoading indicators, cached API responses, and background re-fetching are not\na good fit for either Context or Redux. React Query, SWR, or RTK Query are\npurpose-built for this; layering server state into a Redux store adds\ncomplexity with no gain.",[10,1306,1308],{"id":1307},"what-a-great-interview-answer-looks-like","What a Great Interview Answer Looks Like",[15,1310,1311,1312,1315],{},"When an interviewer asks \"Context vs Redux?\", they are not looking for a\nfeature comparison. They are looking for evidence that you understand the\n",[22,1313,1314],{},"mechanics"," — re-render semantics, selector granularity, middleware — and that\nyou can reason about trade-offs rather than recite a preference.",[15,1317,1318],{},"A strong answer has four parts:",[1320,1321,1322,1328,1334,1343],"ol",{},[1237,1323,1324,1327],{},[34,1325,1326],{},"Clarify what Context is"," — a transport mechanism, not a state manager.\nIt shares a value; it does not define how that value is updated or structured.",[1237,1329,1330,1333],{},[34,1331,1332],{},"Name the re-render problem"," — any change to the context value re-renders\nall consumers. There is no selector layer to limit this. For infrequently\nupdated values this is fine; for frequent updates it becomes a bottleneck.",[1237,1335,1336,1339,1340,1342],{},[34,1337,1338],{},"Explain what Redux adds"," — middleware for async, DevTools for debugging,\n",[42,1341,613],{}," for granular subscriptions. These are the specific gaps Context\ncannot fill without third-party libraries.",[1237,1344,1345,1348],{},[34,1346,1347],{},"Give a concrete decision heuristic"," — start with local state, promote to\nContext for ambient global data (theme, auth, locale), reach for Redux when\nyour state has behaviour that Context can't model: async sequences, derived\ncomputed values, audit trails. Mention Zustand\u002FJotai as a pragmatic middle\nground.",[15,1350,1351],{},"Interviewers who ask this question have usually been burned by a Context\nanti-pattern in production — probably a large monolithic context that triggered\ncascading re-renders across the whole tree. Show them you understand why that\nhappens and how you'd prevent it, and you'll stand out from candidates who\nonly know the surface-level talking points.",[1353,1354,1355],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":64,"searchDepth":104,"depth":104,"links":1357},[1358,1359,1360,1361,1366,1367,1368],{"id":12,"depth":104,"text":13},{"id":28,"depth":104,"text":29},{"id":305,"depth":104,"text":306},{"id":624,"depth":104,"text":625,"children":1362},[1363,1364,1365],{"id":632,"depth":111,"text":633},{"id":767,"depth":111,"text":768},{"id":559,"depth":111,"text":777},{"id":896,"depth":104,"text":897},{"id":1223,"depth":104,"text":1224},{"id":1307,"depth":104,"text":1308},"Context vs Redux for React interviews — understand re-render behavior, performance trade-offs, and when each solution is the right choice.","medium","md","React","react",{},"\u002Fblog\u002Freact-context-vs-redux-guide","\u002Freact\u002Fstate-management\u002Fcontext-vs-redux",{"title":5,"description":1369},"blog\u002Freact-context-vs-redux-guide","Context vs Redux","State Management","state-management","2026-06-24","1ShVRogDh7DO7hwtkdTlAQrm89JSUGzkM03Fmg8kACA",1782244083220]