[{"data":1,"prerenderedAt":1564},["ShallowReactive",2],{"blog-\u002Fblog\u002Freact-redux-toolkit-guide":3},{"id":4,"title":5,"body":6,"description":1549,"difficulty":1550,"extension":1551,"framework":1552,"frameworkSlug":1553,"meta":1554,"navigation":114,"order":60,"path":1555,"qaPath":1556,"seo":1557,"stem":1558,"subtopic":1559,"topic":1560,"topicSlug":1561,"updated":1562,"__hash__":1563},"blog\u002Fblog\u002Freact-redux-toolkit-guide.md","Redux Toolkit in React — Complete Interview Guide",{"type":7,"value":8,"toc":1541},"minimark",[9,18,23,47,235,258,262,281,500,503,514,575,584,588,608,760,767,926,940,947,1036,1040,1050,1056,1293,1307,1310,1455,1458,1462,1465,1470,1484,1489,1508,1518,1522,1528,1537],[10,11,12,13,17],"p",{},"Redux has a reputation for boilerplate. Three files for a single counter —\naction type constants, action creator functions, and a hand-written reducer\nwith spread operators everywhere — made plain Redux painful to maintain. ",[14,15,16],"strong",{},"Redux\nToolkit (RTK)"," is the official answer to that problem. It ships as the\nrecommended way to write Redux code, and it is what interviewers expect you to\nreach for today. Understanding what RTK removes, what it adds, and when it is\n(and isn't) the right tool is the fastest way to impress in a senior React\ninterview.",[19,20,22],"h2",{"id":21},"setting-up-the-store","Setting Up the Store",[10,24,25,26,32,33,36,37,40,41,46],{},"Everything starts with ",[14,27,28],{},[29,30,31],"code",{},"configureStore",". It replaces the old ",[29,34,35],{},"createStore","\ncall and adds two things automatically: ",[14,38,39],{},"Redux DevTools Extension"," support in\ndevelopment, and the ",[14,42,43],{},[29,44,45],{},"redux-thunk"," middleware for async action creators.",[48,49,54],"pre",{"className":50,"code":51,"language":52,"meta":53,"style":53},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F store\u002Fstore.ts\nimport { configureStore } from '@reduxjs\u002Ftoolkit'\nimport counterReducer from '..\u002Ffeatures\u002Fcounter\u002FcounterSlice'\nimport userReducer from '..\u002Ffeatures\u002Fuser\u002FuserSlice'\n\nexport const store = configureStore({\n  reducer: {\n    counter: counterReducer,  \u002F\u002F state.counter\n    user: userReducer,        \u002F\u002F state.user\n  },\n  \u002F\u002F middleware and devTools are wired automatically — override only when needed\n})\n\n\u002F\u002F Export inferred types for TypeScript consumers\nexport type RootState = ReturnType\u003Ctypeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n","js","",[29,55,56,65,83,96,109,116,139,145,154,163,169,175,181,186,192,217],{"__ignoreMap":53},[57,58,61],"span",{"class":59,"line":60},"line",1,[57,62,64],{"class":63},"sJ8bj","\u002F\u002F store\u002Fstore.ts\n",[57,66,68,72,76,79],{"class":59,"line":67},2,[57,69,71],{"class":70},"szBVR","import",[57,73,75],{"class":74},"sVt8B"," { configureStore } ",[57,77,78],{"class":70},"from",[57,80,82],{"class":81},"sZZnC"," '@reduxjs\u002Ftoolkit'\n",[57,84,86,88,91,93],{"class":59,"line":85},3,[57,87,71],{"class":70},[57,89,90],{"class":74}," counterReducer ",[57,92,78],{"class":70},[57,94,95],{"class":81}," '..\u002Ffeatures\u002Fcounter\u002FcounterSlice'\n",[57,97,99,101,104,106],{"class":59,"line":98},4,[57,100,71],{"class":70},[57,102,103],{"class":74}," userReducer ",[57,105,78],{"class":70},[57,107,108],{"class":81}," '..\u002Ffeatures\u002Fuser\u002FuserSlice'\n",[57,110,112],{"class":59,"line":111},5,[57,113,115],{"emptyLinePlaceholder":114},true,"\n",[57,117,119,122,125,129,132,136],{"class":59,"line":118},6,[57,120,121],{"class":70},"export",[57,123,124],{"class":70}," const",[57,126,128],{"class":127},"sj4cs"," store",[57,130,131],{"class":70}," =",[57,133,135],{"class":134},"sScJk"," configureStore",[57,137,138],{"class":74},"({\n",[57,140,142],{"class":59,"line":141},7,[57,143,144],{"class":74},"  reducer: {\n",[57,146,148,151],{"class":59,"line":147},8,[57,149,150],{"class":74},"    counter: counterReducer,  ",[57,152,153],{"class":63},"\u002F\u002F state.counter\n",[57,155,157,160],{"class":59,"line":156},9,[57,158,159],{"class":74},"    user: userReducer,        ",[57,161,162],{"class":63},"\u002F\u002F state.user\n",[57,164,166],{"class":59,"line":165},10,[57,167,168],{"class":74},"  },\n",[57,170,172],{"class":59,"line":171},11,[57,173,174],{"class":63},"  \u002F\u002F middleware and devTools are wired automatically — override only when needed\n",[57,176,178],{"class":59,"line":177},12,[57,179,180],{"class":74},"})\n",[57,182,184],{"class":59,"line":183},13,[57,185,115],{"emptyLinePlaceholder":114},[57,187,189],{"class":59,"line":188},14,[57,190,191],{"class":63},"\u002F\u002F Export inferred types for TypeScript consumers\n",[57,193,195,197,200,203,205,208,211,214],{"class":59,"line":194},15,[57,196,121],{"class":70},[57,198,199],{"class":70}," type",[57,201,202],{"class":134}," RootState",[57,204,131],{"class":70},[57,206,207],{"class":134}," ReturnType",[57,209,210],{"class":74},"\u003C",[57,212,213],{"class":70},"typeof",[57,215,216],{"class":74}," store.getState>\n",[57,218,220,222,224,227,229,232],{"class":59,"line":219},16,[57,221,121],{"class":70},[57,223,199],{"class":70},[57,225,226],{"class":134}," AppDispatch",[57,228,131],{"class":70},[57,230,231],{"class":70}," typeof",[57,233,234],{"class":74}," store.dispatch\n",[10,236,237,238,241,242,245,246,249,250,253,254,257],{},"Wrap your app in ",[29,239,240],{},"\u003CProvider store={store}>"," from ",[29,243,244],{},"react-redux",". In TypeScript\nprojects, create a ",[29,247,248],{},"hooks.ts"," file that exports typed ",[29,251,252],{},"useAppSelector"," and\n",[29,255,256],{},"useAppDispatch"," wrappers so components get full autocomplete without repeating\ntype annotations everywhere.",[19,259,261],{"id":260},"slices-the-core-primitive","Slices — The Core Primitive",[10,263,264,265,268,269,272,273,276,277,280],{},"A ",[14,266,267],{},"slice"," is a self-contained unit of Redux state. ",[29,270,271],{},"createSlice"," takes a\nname, an initial state, and a ",[29,274,275],{},"reducers"," object, and returns a reducer function\nplus auto-generated action creators. Under the hood it wraps every reducer in\n",[14,278,279],{},"Immer",", so you write code that looks like mutation but produces a new\nimmutable state.",[48,282,284],{"className":50,"code":283,"language":52,"meta":53,"style":53},"\u002F\u002F features\u002Fcounter\u002FcounterSlice.ts\nimport { createSlice } from '@reduxjs\u002Ftoolkit'\n\nconst counterSlice = createSlice({\n  name: 'counter',                    \u002F\u002F prefixes action type strings: 'counter\u002Fincrement'\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value++                   \u002F\u002F Immer draft — no spread needed\n    },\n    decrement(state) {\n      state.value--\n    },\n    incrementBy(state, action) {\n      state.value += action.payload   \u002F\u002F payload is the argument passed to the creator\n    },\n  },\n})\n\nexport const { increment, decrement, incrementBy } = counterSlice.actions\nexport default counterSlice.reducer   \u002F\u002F plug into configureStore\n",[29,285,286,291,302,306,321,335,346,351,366,377,382,393,400,404,421,435,439,444,449,454,486],{"__ignoreMap":53},[57,287,288],{"class":59,"line":60},[57,289,290],{"class":63},"\u002F\u002F features\u002Fcounter\u002FcounterSlice.ts\n",[57,292,293,295,298,300],{"class":59,"line":67},[57,294,71],{"class":70},[57,296,297],{"class":74}," { createSlice } ",[57,299,78],{"class":70},[57,301,82],{"class":81},[57,303,304],{"class":59,"line":85},[57,305,115],{"emptyLinePlaceholder":114},[57,307,308,311,314,316,319],{"class":59,"line":98},[57,309,310],{"class":70},"const",[57,312,313],{"class":127}," counterSlice",[57,315,131],{"class":70},[57,317,318],{"class":134}," createSlice",[57,320,138],{"class":74},[57,322,323,326,329,332],{"class":59,"line":111},[57,324,325],{"class":74},"  name: ",[57,327,328],{"class":81},"'counter'",[57,330,331],{"class":74},",                    ",[57,333,334],{"class":63},"\u002F\u002F prefixes action type strings: 'counter\u002Fincrement'\n",[57,336,337,340,343],{"class":59,"line":118},[57,338,339],{"class":74},"  initialState: { value: ",[57,341,342],{"class":127},"0",[57,344,345],{"class":74}," },\n",[57,347,348],{"class":59,"line":141},[57,349,350],{"class":74},"  reducers: {\n",[57,352,353,356,359,363],{"class":59,"line":147},[57,354,355],{"class":134},"    increment",[57,357,358],{"class":74},"(",[57,360,362],{"class":361},"s4XuR","state",[57,364,365],{"class":74},") {\n",[57,367,368,371,374],{"class":59,"line":156},[57,369,370],{"class":74},"      state.value",[57,372,373],{"class":70},"++",[57,375,376],{"class":63},"                   \u002F\u002F Immer draft — no spread needed\n",[57,378,379],{"class":59,"line":165},[57,380,381],{"class":74},"    },\n",[57,383,384,387,389,391],{"class":59,"line":171},[57,385,386],{"class":134},"    decrement",[57,388,358],{"class":74},[57,390,362],{"class":361},[57,392,365],{"class":74},[57,394,395,397],{"class":59,"line":177},[57,396,370],{"class":74},[57,398,399],{"class":70},"--\n",[57,401,402],{"class":59,"line":183},[57,403,381],{"class":74},[57,405,406,409,411,413,416,419],{"class":59,"line":188},[57,407,408],{"class":134},"    incrementBy",[57,410,358],{"class":74},[57,412,362],{"class":361},[57,414,415],{"class":74},", ",[57,417,418],{"class":361},"action",[57,420,365],{"class":74},[57,422,423,426,429,432],{"class":59,"line":194},[57,424,425],{"class":74},"      state.value ",[57,427,428],{"class":70},"+=",[57,430,431],{"class":74}," action.payload   ",[57,433,434],{"class":63},"\u002F\u002F payload is the argument passed to the creator\n",[57,436,437],{"class":59,"line":219},[57,438,381],{"class":74},[57,440,442],{"class":59,"line":441},17,[57,443,168],{"class":74},[57,445,447],{"class":59,"line":446},18,[57,448,180],{"class":74},[57,450,452],{"class":59,"line":451},19,[57,453,115],{"emptyLinePlaceholder":114},[57,455,457,459,461,464,467,469,472,474,477,480,483],{"class":59,"line":456},20,[57,458,121],{"class":70},[57,460,124],{"class":70},[57,462,463],{"class":74}," { ",[57,465,466],{"class":127},"increment",[57,468,415],{"class":74},[57,470,471],{"class":127},"decrement",[57,473,415],{"class":74},[57,475,476],{"class":127},"incrementBy",[57,478,479],{"class":74}," } ",[57,481,482],{"class":70},"=",[57,484,485],{"class":74}," counterSlice.actions\n",[57,487,489,491,494,497],{"class":59,"line":488},21,[57,490,121],{"class":70},[57,492,493],{"class":70}," default",[57,495,496],{"class":74}," counterSlice.reducer   ",[57,498,499],{"class":63},"\u002F\u002F plug into configureStore\n",[10,501,502],{},"The Immer integration is the most important productivity win in RTK. Deeply\nnested updates — which required nested spread objects in plain Redux — become\nstraightforward property assignments on the draft. The one constraint: you must\neither mutate the draft or return a replacement value, never both.",[10,504,505,506,509,510,513],{},"In components, read state with ",[29,507,508],{},"useSelector"," and write with ",[29,511,512],{},"useDispatch",":",[48,515,519],{"className":516,"code":517,"language":518,"meta":53,"style":53},"language-jsx shiki shiki-themes github-light github-dark","const count = useSelector(state => state.counter.value)\nconst dispatch = useDispatch()\ndispatch(incrementBy(5))\n","jsx",[29,520,521,543,558],{"__ignoreMap":53},[57,522,523,525,528,530,533,535,537,540],{"class":59,"line":60},[57,524,310],{"class":70},[57,526,527],{"class":127}," count",[57,529,131],{"class":70},[57,531,532],{"class":134}," useSelector",[57,534,358],{"class":74},[57,536,362],{"class":361},[57,538,539],{"class":70}," =>",[57,541,542],{"class":74}," state.counter.value)\n",[57,544,545,547,550,552,555],{"class":59,"line":67},[57,546,310],{"class":70},[57,548,549],{"class":127}," dispatch",[57,551,131],{"class":70},[57,553,554],{"class":134}," useDispatch",[57,556,557],{"class":74},"()\n",[57,559,560,563,565,567,569,572],{"class":59,"line":85},[57,561,562],{"class":134},"dispatch",[57,564,358],{"class":74},[57,566,476],{"class":134},[57,568,358],{"class":74},[57,570,571],{"class":127},"5",[57,573,574],{"class":74},"))\n",[10,576,577,578,583],{},"For derived values — filtered lists, aggregates, anything that builds a new\narray or object — reach for ",[14,579,580],{},[29,581,582],{},"createSelector"," from RTK. It memoizes the\nresult and prevents unnecessary re-renders when the underlying data hasn't\nactually changed.",[19,585,587],{"id":586},"async-logic-with-createasyncthunk","Async Logic with createAsyncThunk",[10,589,590,591,596,597,415,600,603,604,607],{},"Fetching data is where plain Redux got especially painful. RTK's answer is\n",[14,592,593],{},[29,594,595],{},"createAsyncThunk",": you give it an action type prefix and an async function\nthat returns the payload. It automatically dispatches ",[29,598,599],{},"pending",[29,601,602],{},"fulfilled",",\nand ",[29,605,606],{},"rejected"," actions around your async call.",[48,609,611],{"className":50,"code":610,"language":52,"meta":53,"style":53},"\u002F\u002F features\u002Fuser\u002FuserThunks.ts\nimport { createAsyncThunk } from '@reduxjs\u002Ftoolkit'\n\nexport const fetchUser = createAsyncThunk('user\u002FfetchById', async (userId: number) => {\n  const res = await fetch(`\u002Fapi\u002Fusers\u002F${userId}`)\n  if (!res.ok) throw new Error('User not found')  \u002F\u002F throw to trigger rejected\n  return res.json()                                \u002F\u002F return value becomes action.payload\n})\n",[29,612,613,618,629,633,677,706,739,756],{"__ignoreMap":53},[57,614,615],{"class":59,"line":60},[57,616,617],{"class":63},"\u002F\u002F features\u002Fuser\u002FuserThunks.ts\n",[57,619,620,622,625,627],{"class":59,"line":67},[57,621,71],{"class":70},[57,623,624],{"class":74}," { createAsyncThunk } ",[57,626,78],{"class":70},[57,628,82],{"class":81},[57,630,631],{"class":59,"line":85},[57,632,115],{"emptyLinePlaceholder":114},[57,634,635,637,639,642,644,647,649,652,654,657,660,663,665,668,671,674],{"class":59,"line":98},[57,636,121],{"class":70},[57,638,124],{"class":70},[57,640,641],{"class":127}," fetchUser",[57,643,131],{"class":70},[57,645,646],{"class":134}," createAsyncThunk",[57,648,358],{"class":74},[57,650,651],{"class":81},"'user\u002FfetchById'",[57,653,415],{"class":74},[57,655,656],{"class":70},"async",[57,658,659],{"class":74}," (",[57,661,662],{"class":361},"userId",[57,664,513],{"class":70},[57,666,667],{"class":127}," number",[57,669,670],{"class":74},") ",[57,672,673],{"class":70},"=>",[57,675,676],{"class":74}," {\n",[57,678,679,682,685,687,690,693,695,698,700,703],{"class":59,"line":111},[57,680,681],{"class":70},"  const",[57,683,684],{"class":127}," res",[57,686,131],{"class":70},[57,688,689],{"class":70}," await",[57,691,692],{"class":134}," fetch",[57,694,358],{"class":74},[57,696,697],{"class":81},"`\u002Fapi\u002Fusers\u002F${",[57,699,662],{"class":74},[57,701,702],{"class":81},"}`",[57,704,705],{"class":74},")\n",[57,707,708,711,713,716,719,722,725,728,730,733,736],{"class":59,"line":118},[57,709,710],{"class":70},"  if",[57,712,659],{"class":74},[57,714,715],{"class":70},"!",[57,717,718],{"class":74},"res.ok) ",[57,720,721],{"class":70},"throw",[57,723,724],{"class":70}," new",[57,726,727],{"class":134}," Error",[57,729,358],{"class":74},[57,731,732],{"class":81},"'User not found'",[57,734,735],{"class":74},")  ",[57,737,738],{"class":63},"\u002F\u002F throw to trigger rejected\n",[57,740,741,744,747,750,753],{"class":59,"line":141},[57,742,743],{"class":70},"  return",[57,745,746],{"class":74}," res.",[57,748,749],{"class":134},"json",[57,751,752],{"class":74},"()                                ",[57,754,755],{"class":63},"\u002F\u002F return value becomes action.payload\n",[57,757,758],{"class":59,"line":147},[57,759,180],{"class":74},[10,761,762,763,766],{},"Handle the lifecycle in ",[29,764,765],{},"extraReducers"," using the builder pattern:",[48,768,770],{"className":50,"code":769,"language":52,"meta":53,"style":53},"extraReducers(builder) {\n  builder\n    .addCase(fetchUser.pending, (state) => {\n      state.status = 'loading'\n      state.error = null\n    })\n    .addCase(fetchUser.fulfilled, (state, action) => {\n      state.status = 'succeeded'\n      state.data = action.payload\n    })\n    .addCase(fetchUser.rejected, (state, action) => {\n      state.status = 'failed'\n      state.error = action.error.message ?? 'Unknown error'\n    })\n}\n",[29,771,772,779,784,803,813,823,828,849,858,868,872,893,902,917,921],{"__ignoreMap":53},[57,773,774,776],{"class":59,"line":60},[57,775,765],{"class":134},[57,777,778],{"class":74},"(builder) {\n",[57,780,781],{"class":59,"line":67},[57,782,783],{"class":74},"  builder\n",[57,785,786,789,792,795,797,799,801],{"class":59,"line":85},[57,787,788],{"class":74},"    .",[57,790,791],{"class":134},"addCase",[57,793,794],{"class":74},"(fetchUser.pending, (",[57,796,362],{"class":361},[57,798,670],{"class":74},[57,800,673],{"class":70},[57,802,676],{"class":74},[57,804,805,808,810],{"class":59,"line":98},[57,806,807],{"class":74},"      state.status ",[57,809,482],{"class":70},[57,811,812],{"class":81}," 'loading'\n",[57,814,815,818,820],{"class":59,"line":111},[57,816,817],{"class":74},"      state.error ",[57,819,482],{"class":70},[57,821,822],{"class":127}," null\n",[57,824,825],{"class":59,"line":118},[57,826,827],{"class":74},"    })\n",[57,829,830,832,834,837,839,841,843,845,847],{"class":59,"line":141},[57,831,788],{"class":74},[57,833,791],{"class":134},[57,835,836],{"class":74},"(fetchUser.fulfilled, (",[57,838,362],{"class":361},[57,840,415],{"class":74},[57,842,418],{"class":361},[57,844,670],{"class":74},[57,846,673],{"class":70},[57,848,676],{"class":74},[57,850,851,853,855],{"class":59,"line":147},[57,852,807],{"class":74},[57,854,482],{"class":70},[57,856,857],{"class":81}," 'succeeded'\n",[57,859,860,863,865],{"class":59,"line":156},[57,861,862],{"class":74},"      state.data ",[57,864,482],{"class":70},[57,866,867],{"class":74}," action.payload\n",[57,869,870],{"class":59,"line":165},[57,871,827],{"class":74},[57,873,874,876,878,881,883,885,887,889,891],{"class":59,"line":171},[57,875,788],{"class":74},[57,877,791],{"class":134},[57,879,880],{"class":74},"(fetchUser.rejected, (",[57,882,362],{"class":361},[57,884,415],{"class":74},[57,886,418],{"class":361},[57,888,670],{"class":74},[57,890,673],{"class":70},[57,892,676],{"class":74},[57,894,895,897,899],{"class":59,"line":177},[57,896,807],{"class":74},[57,898,482],{"class":70},[57,900,901],{"class":81}," 'failed'\n",[57,903,904,906,908,911,914],{"class":59,"line":183},[57,905,817],{"class":74},[57,907,482],{"class":70},[57,909,910],{"class":74}," action.error.message ",[57,912,913],{"class":70},"??",[57,915,916],{"class":81}," 'Unknown error'\n",[57,918,919],{"class":59,"line":188},[57,920,827],{"class":74},[57,922,923],{"class":59,"line":194},[57,924,925],{"class":74},"}\n",[10,927,928,929,932,933,253,936,939],{},"A critical convention: model loading state as a string (",[29,930,931],{},"'idle' | 'loading' | 'succeeded' | 'failed'",") rather than a boolean. A boolean cannot represent all\nfour states without impossible combinations like ",[29,934,935],{},"isLoading: true",[29,937,938],{},"isError: true"," simultaneously.",[10,941,942,943,946],{},"In components, dispatch the thunk and call ",[29,944,945],{},".unwrap()"," if you need to handle\nerrors locally:",[48,948,950],{"className":516,"code":949,"language":518,"meta":53,"style":53},"try {\n  const user = await dispatch(fetchUser(42)).unwrap()\n  navigate(`\u002Fprofile\u002F${user.id}`)\n} catch (err) {\n  setFormError(err.message)\n}\n",[29,951,952,959,990,1013,1024,1032],{"__ignoreMap":53},[57,953,954,957],{"class":59,"line":60},[57,955,956],{"class":70},"try",[57,958,676],{"class":74},[57,960,961,963,966,968,970,972,974,977,979,982,985,988],{"class":59,"line":67},[57,962,681],{"class":70},[57,964,965],{"class":127}," user",[57,967,131],{"class":70},[57,969,689],{"class":70},[57,971,549],{"class":134},[57,973,358],{"class":74},[57,975,976],{"class":134},"fetchUser",[57,978,358],{"class":74},[57,980,981],{"class":127},"42",[57,983,984],{"class":74},")).",[57,986,987],{"class":134},"unwrap",[57,989,557],{"class":74},[57,991,992,995,997,1000,1003,1006,1009,1011],{"class":59,"line":85},[57,993,994],{"class":134},"  navigate",[57,996,358],{"class":74},[57,998,999],{"class":81},"`\u002Fprofile\u002F${",[57,1001,1002],{"class":74},"user",[57,1004,1005],{"class":81},".",[57,1007,1008],{"class":74},"id",[57,1010,702],{"class":81},[57,1012,705],{"class":74},[57,1014,1015,1018,1021],{"class":59,"line":98},[57,1016,1017],{"class":74},"} ",[57,1019,1020],{"class":70},"catch",[57,1022,1023],{"class":74}," (err) {\n",[57,1025,1026,1029],{"class":59,"line":111},[57,1027,1028],{"class":134},"  setFormError",[57,1030,1031],{"class":74},"(err.message)\n",[57,1033,1034],{"class":59,"line":118},[57,1035,925],{"class":74},[19,1037,1039],{"id":1038},"rtk-query-for-server-state","RTK Query for Server State",[10,1041,1042,1043,1045,1046,1049],{},"For data that lives on a server — lists, single items, paginated results —\n",[29,1044,595],{}," still requires you to write every slice manually. ",[14,1047,1048],{},"RTK\nQuery"," is a complete data-fetching layer built on top of Redux that handles\ncaching, deduplication, background refetching, and cache invalidation for you.",[10,1051,1052,1053,513],{},"Define an API with ",[29,1054,1055],{},"createApi",[48,1057,1059],{"className":50,"code":1058,"language":52,"meta":53,"style":53},"\u002F\u002F features\u002Fposts\u002FpostsApi.ts\nimport { createApi, fetchBaseQuery } from '@reduxjs\u002Ftoolkit\u002Fquery\u002Freact'\n\nexport const postsApi = createApi({\n  reducerPath: 'postsApi',              \u002F\u002F key in Redux state\n  baseQuery: fetchBaseQuery({ baseUrl: '\u002Fapi' }),\n  tagTypes: ['Post'],\n  endpoints: (builder) => ({\n    getPosts: builder.query({\n      query: () => '\u002Fposts',\n      providesTags: ['Post'],           \u002F\u002F cache tagged so mutations can bust it\n    }),\n    createPost: builder.mutation({\n      query: (body) => ({ url: '\u002Fposts', method: 'POST', body }),\n      invalidatesTags: ['Post'],        \u002F\u002F refetch getPosts automatically on success\n    }),\n  }),\n})\n\nexport const { useGetPostsQuery, useCreatePostMutation } = postsApi\n",[29,1060,1061,1066,1078,1082,1098,1112,1129,1140,1158,1168,1184,1197,1202,1212,1240,1253,1257,1262,1266,1270],{"__ignoreMap":53},[57,1062,1063],{"class":59,"line":60},[57,1064,1065],{"class":63},"\u002F\u002F features\u002Fposts\u002FpostsApi.ts\n",[57,1067,1068,1070,1073,1075],{"class":59,"line":67},[57,1069,71],{"class":70},[57,1071,1072],{"class":74}," { createApi, fetchBaseQuery } ",[57,1074,78],{"class":70},[57,1076,1077],{"class":81}," '@reduxjs\u002Ftoolkit\u002Fquery\u002Freact'\n",[57,1079,1080],{"class":59,"line":85},[57,1081,115],{"emptyLinePlaceholder":114},[57,1083,1084,1086,1088,1091,1093,1096],{"class":59,"line":98},[57,1085,121],{"class":70},[57,1087,124],{"class":70},[57,1089,1090],{"class":127}," postsApi",[57,1092,131],{"class":70},[57,1094,1095],{"class":134}," createApi",[57,1097,138],{"class":74},[57,1099,1100,1103,1106,1109],{"class":59,"line":111},[57,1101,1102],{"class":74},"  reducerPath: ",[57,1104,1105],{"class":81},"'postsApi'",[57,1107,1108],{"class":74},",              ",[57,1110,1111],{"class":63},"\u002F\u002F key in Redux state\n",[57,1113,1114,1117,1120,1123,1126],{"class":59,"line":118},[57,1115,1116],{"class":74},"  baseQuery: ",[57,1118,1119],{"class":134},"fetchBaseQuery",[57,1121,1122],{"class":74},"({ baseUrl: ",[57,1124,1125],{"class":81},"'\u002Fapi'",[57,1127,1128],{"class":74}," }),\n",[57,1130,1131,1134,1137],{"class":59,"line":141},[57,1132,1133],{"class":74},"  tagTypes: [",[57,1135,1136],{"class":81},"'Post'",[57,1138,1139],{"class":74},"],\n",[57,1141,1142,1145,1148,1151,1153,1155],{"class":59,"line":147},[57,1143,1144],{"class":134},"  endpoints",[57,1146,1147],{"class":74},": (",[57,1149,1150],{"class":361},"builder",[57,1152,670],{"class":74},[57,1154,673],{"class":70},[57,1156,1157],{"class":74}," ({\n",[57,1159,1160,1163,1166],{"class":59,"line":156},[57,1161,1162],{"class":74},"    getPosts: builder.",[57,1164,1165],{"class":134},"query",[57,1167,138],{"class":74},[57,1169,1170,1173,1176,1178,1181],{"class":59,"line":165},[57,1171,1172],{"class":134},"      query",[57,1174,1175],{"class":74},": () ",[57,1177,673],{"class":70},[57,1179,1180],{"class":81}," '\u002Fposts'",[57,1182,1183],{"class":74},",\n",[57,1185,1186,1189,1191,1194],{"class":59,"line":171},[57,1187,1188],{"class":74},"      providesTags: [",[57,1190,1136],{"class":81},[57,1192,1193],{"class":74},"],           ",[57,1195,1196],{"class":63},"\u002F\u002F cache tagged so mutations can bust it\n",[57,1198,1199],{"class":59,"line":177},[57,1200,1201],{"class":74},"    }),\n",[57,1203,1204,1207,1210],{"class":59,"line":183},[57,1205,1206],{"class":74},"    createPost: builder.",[57,1208,1209],{"class":134},"mutation",[57,1211,138],{"class":74},[57,1213,1214,1216,1218,1221,1223,1225,1228,1231,1234,1237],{"class":59,"line":188},[57,1215,1172],{"class":134},[57,1217,1147],{"class":74},[57,1219,1220],{"class":361},"body",[57,1222,670],{"class":74},[57,1224,673],{"class":70},[57,1226,1227],{"class":74}," ({ url: ",[57,1229,1230],{"class":81},"'\u002Fposts'",[57,1232,1233],{"class":74},", method: ",[57,1235,1236],{"class":81},"'POST'",[57,1238,1239],{"class":74},", body }),\n",[57,1241,1242,1245,1247,1250],{"class":59,"line":194},[57,1243,1244],{"class":74},"      invalidatesTags: [",[57,1246,1136],{"class":81},[57,1248,1249],{"class":74},"],        ",[57,1251,1252],{"class":63},"\u002F\u002F refetch getPosts automatically on success\n",[57,1254,1255],{"class":59,"line":219},[57,1256,1201],{"class":74},[57,1258,1259],{"class":59,"line":441},[57,1260,1261],{"class":74},"  }),\n",[57,1263,1264],{"class":59,"line":446},[57,1265,180],{"class":74},[57,1267,1268],{"class":59,"line":451},[57,1269,115],{"emptyLinePlaceholder":114},[57,1271,1272,1274,1276,1278,1281,1283,1286,1288,1290],{"class":59,"line":456},[57,1273,121],{"class":70},[57,1275,124],{"class":70},[57,1277,463],{"class":74},[57,1279,1280],{"class":127},"useGetPostsQuery",[57,1282,415],{"class":74},[57,1284,1285],{"class":127},"useCreatePostMutation",[57,1287,479],{"class":74},[57,1289,482],{"class":70},[57,1291,1292],{"class":74}," postsApi\n",[10,1294,1295,1296,1299,1300,1302,1303,1306],{},"Add ",[29,1297,1298],{},"postsApi.reducer"," to ",[29,1301,31],{},"'s reducer map and\n",[29,1304,1305],{},"postsApi.middleware"," to the middleware chain — RTK Query uses middleware to\nmanage subscription lifetimes and cache expiry.",[10,1308,1309],{},"In components, hooks replace all the manual loading\u002Ferror logic:",[48,1311,1313],{"className":516,"code":1312,"language":518,"meta":53,"style":53},"function PostsList() {\n  const { data: posts = [], isLoading, isError } = useGetPostsQuery()\n\n  if (isLoading) return \u003CSpinner \u002F>\n  if (isError) return \u003Cp>Failed to load posts.\u003C\u002Fp>\n  return \u003Cul>{posts.map(p => \u003Cli key={p.id}>{p.title}\u003C\u002Fli>)}\u003C\u002Ful>\n}\n",[29,1314,1315,1326,1363,1367,1386,1408,1451],{"__ignoreMap":53},[57,1316,1317,1320,1323],{"class":59,"line":60},[57,1318,1319],{"class":70},"function",[57,1321,1322],{"class":134}," PostsList",[57,1324,1325],{"class":74},"() {\n",[57,1327,1328,1330,1332,1335,1338,1341,1343,1346,1349,1351,1354,1356,1358,1361],{"class":59,"line":67},[57,1329,681],{"class":70},[57,1331,463],{"class":74},[57,1333,1334],{"class":361},"data",[57,1336,1337],{"class":74},": ",[57,1339,1340],{"class":127},"posts",[57,1342,131],{"class":70},[57,1344,1345],{"class":74}," [], ",[57,1347,1348],{"class":127},"isLoading",[57,1350,415],{"class":74},[57,1352,1353],{"class":127},"isError",[57,1355,479],{"class":74},[57,1357,482],{"class":70},[57,1359,1360],{"class":134}," useGetPostsQuery",[57,1362,557],{"class":74},[57,1364,1365],{"class":59,"line":85},[57,1366,115],{"emptyLinePlaceholder":114},[57,1368,1369,1371,1374,1377,1380,1383],{"class":59,"line":98},[57,1370,710],{"class":70},[57,1372,1373],{"class":74}," (isLoading) ",[57,1375,1376],{"class":70},"return",[57,1378,1379],{"class":74}," \u003C",[57,1381,1382],{"class":127},"Spinner",[57,1384,1385],{"class":74}," \u002F>\n",[57,1387,1388,1390,1393,1395,1397,1400,1403,1405],{"class":59,"line":111},[57,1389,710],{"class":70},[57,1391,1392],{"class":74}," (isError) ",[57,1394,1376],{"class":70},[57,1396,1379],{"class":74},[57,1398,10],{"class":1399},"s9eBZ",[57,1401,1402],{"class":74},">Failed to load posts.\u003C\u002F",[57,1404,10],{"class":1399},[57,1406,1407],{"class":74},">\n",[57,1409,1410,1412,1414,1417,1420,1423,1425,1427,1429,1431,1434,1437,1439,1442,1444,1447,1449],{"class":59,"line":118},[57,1411,743],{"class":70},[57,1413,1379],{"class":74},[57,1415,1416],{"class":1399},"ul",[57,1418,1419],{"class":74},">{posts.",[57,1421,1422],{"class":134},"map",[57,1424,358],{"class":74},[57,1426,10],{"class":361},[57,1428,539],{"class":70},[57,1430,1379],{"class":74},[57,1432,1433],{"class":1399},"li",[57,1435,1436],{"class":134}," key",[57,1438,482],{"class":70},[57,1440,1441],{"class":74},"{p.id}>{p.title}\u003C\u002F",[57,1443,1433],{"class":1399},[57,1445,1446],{"class":74},">)}\u003C\u002F",[57,1448,1416],{"class":1399},[57,1450,1407],{"class":74},[57,1452,1453],{"class":59,"line":141},[57,1454,925],{"class":74},[10,1456,1457],{},"The tag-based cache invalidation is RTK Query's most interview-worthy feature.\nBy tagging queries and invalidating those tags in mutations, you get automatic\nUI updates after writes without manually merging cache state.",[19,1459,1461],{"id":1460},"when-redux-is-and-isnt-the-right-tool","When Redux Is (and Isn't) the Right Tool",[10,1463,1464],{},"This is the question senior interviewers most want to hear a nuanced answer to.",[10,1466,1467],{},[14,1468,1469],{},"Use Redux when:",[1416,1471,1472,1475,1478,1481],{},[1433,1473,1474],{},"State is shared across many unrelated components and the component tree is\ntoo deep for prop drilling or too broad for a single Context provider to be\nefficient.",[1433,1476,1477],{},"Update logic is complex — multiple actions can affect the same piece of state,\nor one action must update multiple slices.",[1433,1479,1480],{},"You need time-travel debugging, action replay, or strict audit logging of\nstate transitions.",[1433,1482,1483],{},"The team is large enough that a single, predictable data flow helps\ncoordination.",[10,1485,1486],{},[14,1487,1488],{},"Skip Redux when:",[1416,1490,1491,1502,1505],{},[1433,1492,1493,1494,1497,1498,1501],{},"The state is local to one component or a small subtree — ",[29,1495,1496],{},"useState"," or\n",[29,1499,1500],{},"useReducer"," is simpler and keeps the logic next to the UI.",[1433,1503,1504],{},"The state is entirely server-derived — RTK Query or React Query manages\ncaching, deduplication, and synchronization better than a hand-written async\nslice ever will.",[1433,1506,1507],{},"The app is genuinely small — introducing slices, selectors, action creators,\nand a Provider for a to-do demo adds cognitive weight without any payoff.",[10,1509,1510,1511,1497,1514,1517],{},"A reliable smell: if your slice contains flags like ",[29,1512,1513],{},"isModalOpen",[29,1515,1516],{},"activeTab",", or if only one component dispatches and reads a given action, that\nstate belongs local.",[19,1519,1521],{"id":1520},"what-interviewers-are-looking-for","What Interviewers Are Looking For",[10,1523,1524,1525,1527],{},"When the topic of Redux comes up in an interview, the weakest answer is reciting\nthe pattern. The strongest answer explains trade-offs: why RTK made Redux\npractical, why RTK Query can replace most ",[29,1526,595],{}," slices for server\nstate, and — crucially — why you would choose Zustand or plain Context for\nsimpler global state instead of reaching for Redux by default.",[10,1529,1530,1531,1533,1534,1536],{},"Expect to sketch ",[29,1532,271],{}," with ",[29,1535,765],{}," from memory, explain the\nImmer contract (mutate the draft or return a replacement, not both), and describe\ntag-based cache invalidation in RTK Query at a whiteboard level. Those three\ntopics cover the overwhelming majority of real RTK interview questions.",[1538,1539,1540],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}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 .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 .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":53,"searchDepth":67,"depth":67,"links":1542},[1543,1544,1545,1546,1547,1548],{"id":21,"depth":67,"text":22},{"id":260,"depth":67,"text":261},{"id":586,"depth":67,"text":587},{"id":1038,"depth":67,"text":1039},{"id":1460,"depth":67,"text":1461},{"id":1520,"depth":67,"text":1521},"Master Redux Toolkit for React interviews — createSlice, configureStore, createAsyncThunk, RTK Query, and when to choose Redux over simpler solutions.","medium","md","React","react",{},"\u002Fblog\u002Freact-redux-toolkit-guide","\u002Freact\u002Fstate-management\u002Fredux-toolkit",{"title":5,"description":1549},"blog\u002Freact-redux-toolkit-guide","Redux Toolkit","State Management","state-management","2026-06-24","v7Ur5BOvAWJ0a03DELT9NwUNI1P4fxkqtAhzVjw_HzQ",1782244083220]