🚀 Redux Toolkit Masterclass

Day 27-30: Modern Redux - Complete Guide with Interactive Demos

📚 Redux Toolkit Kya Hai?

Redux Toolkit (RTK): The official, opinionated, batteries-included toolset for efficient Redux development. It's the modern way to write Redux logic, providing utilities that simplify store setup, reducer creation, and immutable update logic.

Simple shabdon mein: Redux Toolkit ek modern package hai jo Redux ko use karna bahut easy bana deta hai. Purane Redux mein bahut zyada boilerplate code likhna padta tha - actions, action creators, reducers, store setup sab manually. RTK ne ye sab automate kar diya! 🎯

Analogy: Socho agar pehle aapko car manually start karni padti thi (crank handle se), ab automatic start button mil gaya. Kaam wahi hai, but process bahut simple ho gaya!

🔄 Redux Flow Visualization

🖱️ User Action

Button Click

→

📤 Dispatch

Send Action

→

⚙️ Reducer

Update State

→

💾 Store

New State

→

🔄 Re-render

UI Update

1️⃣ Why Redux Toolkit? (Purane Redux se Better Kyun?)

❌ Old Redux (Legacy)

  • ❌ Bahut zyada boilerplate code
  • ❌ Manual action types define karna
  • ❌ Action creators manually banana
  • ❌ Immutability manually handle karna
  • ❌ Store setup complex
  • ❌ Middleware manually configure
  • ❌ DevTools manually setup

✅ Redux Toolkit (Modern)

  • Minimal boilerplate code
  • Auto-generated action types
  • Auto-generated action creators
  • Immer.js built-in (mutable syntax)
  • configureStore - one line setup
  • Thunk middleware included
  • DevTools auto-configured

Real numbers: Purane Redux mein ek simple counter banane ke liye ~50-60 lines code likhna padta tha. Redux Toolkit mein same functionality ~15-20 lines mein! 🚀

📊 Code Comparison: Old vs New

// Old Redux - Bahut zyada code! 😰 // 1. Action Types const INCREMENT = 'counter/increment'; const DECREMENT = 'counter/decrement'; // 2. Action Creators function increment() { return { type: INCREMENT }; } function decrement() { return { type: DECREMENT }; } // 3. Reducer const initialState = { value: 0 }; function counterReducer(state = initialState, action) { switch (action.type) { case INCREMENT: return { ...state, value: state.value + 1 }; case DECREMENT: return { ...state, value: state.value - 1 }; default: return state; } } // 4. Store Setup import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import { composeWithDevTools } from 'redux-devtools-extension'; const store = createStore( counterReducer, composeWithDevTools(applyMiddleware(thunk)) ); // Total: ~30 lines for simple counter! 😓
// Redux Toolkit - Clean & Simple! 😍 import { createSlice, configureStore } from '@reduxjs/toolkit'; // 1. Create Slice (combines actions + reducer) const counterSlice = createSlice({ name: 'counter', initialState: { value: 0 }, reducers: { increment: (state) => { state.value += 1; // Looks mutable, but Immer makes it immutable! }, decrement: (state) => { state.value -= 1; } } }); // 2. Export actions (auto-generated!) export const { increment, decrement } = counterSlice.actions; // 3. Configure Store (one line!) const store = configureStore({ reducer: { counter: counterSlice.reducer } }); // Total: ~15 lines! DevTools & Thunk included! 🚀

2️⃣ Core Concepts: createSlice, configureStore

🍰 createSlice

Definition: A function that accepts an initial state, an object of reducer functions, and a "slice name", and automatically generates action creators and action types.

Kya karta hai? Ek hi jagah pe actions aur reducers define karo. RTK automatically action creators aur types bana deta hai!

⚙️ configureStore

Definition: A friendly abstraction over the standard Redux createStore that adds good defaults (Redux DevTools, thunk middleware) automatically.

Kya karta hai? Store setup ko super easy bana deta hai. DevTools aur middleware automatically configure ho jate hain!

🔄 createReducer

Definition: A utility that lets you define reducers as lookup tables of action types to case reducer functions, with built-in Immer support.

Kab use karein? Jab aapko createSlice se zyada control chahiye. Usually createSlice hi kaafi hai!

🎬 createAction

Definition: A helper function for defining Redux action creators with a given action type string.

Kab use karein? Rarely! createSlice automatically actions bana deta hai. Manual control ke liye use karo.

🎯 Live Demo: Counter with Redux Toolkit

0
{ "counter": { "value": 0 } }
// Complete createSlice Example import { createSlice } from '@reduxjs/toolkit'; const counterSlice = createSlice({ name: 'counter', // Slice ka naam initialState: { value: 0, history: [] }, reducers: { // Simple action - no payload increment: (state) => { state.value += 1; state.history.push('increment'); }, // Action with payload incrementByAmount: (state, action) => { state.value += action.payload; state.history.push(`added ${action.payload}`); }, // Complex logic reset: (state) => { state.value = 0; state.history = []; } } }); // Auto-generated actions export const { increment, incrementByAmount, reset } = counterSlice.actions; // Export reducer export default counterSlice.reducer;

3️⃣ Async Thunks - API Calls Handle Karna

createAsyncThunk: A function that accepts a Redux action type string and a callback function that should return a promise. It generates promise lifecycle action types (pending, fulfilled, rejected) and dispatches them based on the promise status.

Problem: Redux mein async operations (API calls) directly nahi kar sakte. Reducers pure functions hone chahiye.

Solution: createAsyncThunk! Ye automatically teen actions generate karta hai - pending (loading), fulfilled (success), rejected (error). Aapko sirf async function likhna hai! 🎯

// createAsyncThunk Example import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; // 1. Create Async Thunk export const fetchUsers = createAsyncThunk( 'users/fetchUsers', // Action type prefix async (userId, thunkAPI) => { try { const response = await fetch(`/api/users/${userId}`); return await response.json(); // Fulfilled payload } catch (error) { return thunkAPI.rejectWithValue(error.message); } } ); // 2. Handle in Slice const usersSlice = createSlice({ name: 'users', initialState: { data: [], loading: false, error: null }, reducers: {}, extraReducers: (builder) => { builder // Pending - API call start hui .addCase(fetchUsers.pending, (state) => { state.loading = true; state.error = null; }) // Fulfilled - Success! .addCase(fetchUsers.fulfilled, (state, action) => { state.loading = false; state.data = action.payload; }) // Rejected - Error! .addCase(fetchUsers.rejected, (state, action) => { state.loading = false; state.error = action.payload; }); } });

🎯 Live Demo: Async Thunk (Fetch Users)

{ "users": { "data": [], "loading": false, "error": null } }
createAsyncThunk automatically teen lifecycle actions generate karta hai. Aapko manually pending/success/error actions nahi banana padta!

4️⃣ RTK Query - Complete Backend Integration

RTK Query: A powerful data fetching and caching tool built on top of Redux Toolkit. It's designed to simplify loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.

Game changer! 🚀 RTK Query ek complete solution hai API calls ke liye. Ye automatically handle karta hai:

  • Data fetching & caching
  • Loading states
  • Error handling
  • Automatic re-fetching
  • Cache invalidation
  • Optimistic updates

Matlab: Aapko manually loading, error, data states manage nahi karne padenge!

📥 GET Requests

Data fetch karne ke liye queries use karo

useQuery

📤 POST/PUT/DELETE

Data modify karne ke liye mutations use karo

useMutation

💾 Auto Caching

Same data dobara fetch nahi hota

Smart Cache

🔄 Auto Refetch

Data automatically update hota hai

Polling
// RTK Query API Setup import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; // 1. Create API Slice export const apiSlice = createApi({ reducerPath: 'api', baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com' }), tagTypes: ['Posts', 'Users'], endpoints: (builder) => ({ // GET - Fetch all posts getPosts: builder.query({ query: () => '/posts', providesTags: ['Posts'] }), // GET - Fetch single post getPost: builder.query({ query: (id) => `/posts/${id}`, providesTags: (result, error, id) => [{ type: 'Posts', id }] }), // POST - Create new post addPost: builder.mutation({ query: (newPost) => ({ url: '/posts', method: 'POST', body: newPost }), invalidatesTags: ['Posts'] // Cache invalidate karo }), // PUT - Update post updatePost: builder.mutation({ query: ({ id, ...patch }) => ({ url: `/posts/${id}`, method: 'PUT', body: patch }), invalidatesTags: (result, error, { id }) => [{ type: 'Posts', id }] }), // DELETE - Delete post deletePost: builder.mutation({ query: (id) => ({ url: `/posts/${id}`, method: 'DELETE' }), invalidatesTags: ['Posts'] }) }) }); // 2. Export hooks (auto-generated!) export const { useGetPostsQuery, useGetPostQuery, useAddPostMutation, useUpdatePostMutation, useDeletePostMutation } = apiSlice; // 3. Use in Component function PostsList() { const { data, isLoading, error } = useGetPostsQuery(); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error!</div>; return ( <div> {data.map(post => <Post key={post.id} {...post} />)} </div> ); }

🎯 Live Demo: RTK Query

Click "Fetch Posts" to load data

🎯 RTK Query Benefits:
  • No manual loading/error states
  • Automatic caching & deduplication
  • Automatic re-fetching on focus/reconnect
  • Optimistic updates support
  • Polling & streaming support
  • TypeScript support out of the box

5️⃣ Normalizing State - Data Ko Efficiently Store Karna

State Normalization: A pattern of organizing data in the Redux store by storing items in a flat structure with IDs as keys, rather than nested arrays. This improves performance and makes updates easier.

Problem: Nested data structures mein updates slow aur complex ho jate hain. Agar aapko ek user update karna hai jo deeply nested hai, toh pura tree traverse karna padega!

Solution: Data ko flat structure mein store karo - entities aur IDs separately. Jaise database mein tables hoti hain!

❌ Nested Structure (Slow)

{ posts: [ { id: 1, title: "Post 1", author: { id: 1, name: "John" }, comments: [ { id: 1, text: "Nice" } ] } ] }

⚠️ Update karna difficult!

✅ Normalized (Fast)

{ posts: { ids: [1], entities: { 1: { id: 1, title: "Post 1", authorId: 1, commentIds: [1] } } }, users: { ids: [1], entities: { 1: { id: 1, name: "John" } } } }

✅ Fast lookups & updates!

// createEntityAdapter - Automatic Normalization! import { createSlice, createEntityAdapter } from '@reduxjs/toolkit'; // 1. Create Entity Adapter const postsAdapter = createEntityAdapter({ selectId: (post) => post.id, // ID field sortComparer: (a, b) => b.date.localeCompare(a.date) // Sort by date }); // 2. Get initial state const initialState = postsAdapter.getInitialState({ loading: false, error: null }); // 3. Create Slice const postsSlice = createSlice({ name: 'posts', initialState, reducers: { // Adapter provides CRUD methods! postAdded: postsAdapter.addOne, postsReceived: postsAdapter.setAll, postUpdated: postsAdapter.updateOne, postRemoved: postsAdapter.removeOne } }); // 4. Export selectors (auto-generated!) export const { selectAll: selectAllPosts, selectById: selectPostById, selectIds: selectPostIds } = postsAdapter.getSelectors((state) => state.posts); // 5. Use in component const posts = useSelector(selectAllPosts); // Get all posts const post = useSelector(state => selectPostById(state, 1)); // Get by ID
createEntityAdapter automatically provides: addOne, addMany, setAll, updateOne, updateMany, removeOne, removeMany, removeAll methods. Aapko manually CRUD logic nahi likhna padta!

6️⃣ Immutable Updates - Immer.js Magic

Immer.js: A library that allows you to work with immutable state in a more convenient way by using "draft" state. Redux Toolkit includes Immer by default, allowing you to write "mutating" logic that actually produces immutable updates.

Purani problem: Redux mein state ko directly mutate nahi kar sakte. Har baar naya object/array banana padta tha using spread operator (...). Complex nested updates bahut messy ho jate the!

RTK solution: Immer.js built-in hai! Aap directly state ko modify kar sakte ho (mutable syntax), but behind the scenes Immer immutable updates kar raha hai! 🎩✨

❌ Manual Immutability (Complex)

// Nested update - Bahut complex! return { ...state, users: { ...state.users, [userId]: { ...state.users[userId], profile: { ...state.users[userId].profile, name: newName } } } }; // Array update return { ...state, todos: state.todos.map(todo => todo.id === id ? { ...todo, completed: true } : todo ) };

✅ Immer (Simple & Clean)

// Same nested update - Super simple! state.users[userId].profile.name = newName; // Array update - Direct! const todo = state.todos.find(t => t.id === id); todo.completed = true; // Array methods work! state.todos.push(newTodo); state.todos.splice(index, 1); state.items.sort((a, b) => a - b);
// Immer in Action - All valid in RTK! const todosSlice = createSlice({ name: 'todos', initialState: { items: [], filter: 'all' }, reducers: { // ✅ Direct mutation (Immer handles it!) todoAdded(state, action) { state.items.push(action.payload); }, // ✅ Direct property update todoToggled(state, action) { const todo = state.items.find(t => t.id === action.payload); if (todo) { todo.completed = !todo.completed; } }, // ✅ Array methods work! todoDeleted(state, action) { const index = state.items.findIndex(t => t.id === action.payload); state.items.splice(index, 1); }, // ✅ Sort in place todosSorted(state) { state.items.sort((a, b) => a.text.localeCompare(b.text)); }, // ⚠️ Can also return new state (both work!) todosCleared(state) { return { items: [], filter: 'all' }; } } });

🎯 Live Demo: Todo List with Immer

No todos yet. Add one!

{ "todos": { "items": [], "filter": "all" } }
⚠️ Important Rules:
  • Either mutate the draft state OR return a new state, not both!
  • Don't return undefined from a reducer (return nothing or new state)
  • Immer only works in reducers, not in thunks or components

7️⃣ Redux DevTools - Time Travel Debugging

Redux DevTools: A browser extension that provides powerful debugging capabilities for Redux applications, including action history, state inspection, time-travel debugging, and action replay.

Superpower! 🦸‍♂️ Redux DevTools se aap time travel kar sakte ho - matlab past mein ja kar dekh sakte ho ki har action ke baad state kaise change hua. Debugging bahut easy ho jati hai!

Best part: RTK mein automatically configured hai. Kuch karna nahi padta!

📜 Action History

Har action ka record dekho chronological order mein

Timeline View

🔍 State Inspector

Current state ko detail mein explore karo

Tree View

⏮️ Time Travel

Kisi bhi previous state pe jump karo

Undo/Redo

🎬 Action Replay

Actions ko skip, replay, ya cancel karo

Debug Mode
// DevTools automatically enabled in RTK! import { configureStore } from '@reduxjs/toolkit'; const store = configureStore({ reducer: { counter: counterReducer, todos: todosReducer } // DevTools enabled by default! 🎉 // No need for composeWithDevTools! }); // Custom DevTools config (optional) const store = configureStore({ reducer: rootReducer, devTools: { name: 'My App', trace: true, // Show stack traces traceLimit: 25 } }); // Disable in production const store = configureStore({ reducer: rootReducer, devTools: process.env.NODE_ENV !== 'production' });
🎯 DevTools Features:
  • Diff View: Dekho state mein kya change hua
  • Action Filtering: Specific actions ko filter karo
  • Export/Import: State ko save aur load karo
  • Persist State: Refresh ke baad bhi state maintain karo
  • Custom Actions: Manually actions dispatch karo

8️⃣ Enterprise Redux Project Structure

Feature-Based Structure: An organizational pattern where Redux logic is grouped by feature/domain rather than by type (actions, reducers, etc.), making the codebase more scalable and maintainable.

Purana tarika: Sab actions ek folder mein, sab reducers ek folder mein - bahut confusing ho jata tha bade apps mein!

Modern tarika: Feature-wise organize karo. Ek feature ki saari cheezein ek jagah - slice, selectors, thunks sab!

📁 Recommended Folder Structure

src/ ├── app/ │ ├── store.js // Configure store │ └── rootReducer.js // Combine reducers │ ├── features/ // Feature-based organization │ ├── auth/ │ │ ├── authSlice.js // Slice (actions + reducer) │ │ ├── authSelectors.js // Reusable selectors │ │ ├── authThunks.js // Async thunks │ │ └── authAPI.js // API calls │ │ │ ├── posts/ │ │ ├── postsSlice.js │ │ ├── postsSelectors.js │ │ ├── PostsList.jsx // Components │ │ └── PostDetail.jsx │ │ │ ├── users/ │ │ ├── usersSlice.js │ │ ├── usersAPI.js │ │ └── UserProfile.jsx │ │ │ └── cart/ │ ├── cartSlice.js │ ├── cartSelectors.js │ └── Cart.jsx │ ├── services/ // RTK Query APIs │ ├── api.js // Base API config │ ├── postsApi.js // Posts endpoints │ └── usersApi.js // Users endpoints │ ├── hooks/ // Custom hooks │ ├── useAuth.js │ └── useAppDispatch.js │ └── utils/ // Helper functions ├── constants.js └── helpers.js
// app/store.js - Central Store Configuration import { configureStore } from '@reduxjs/toolkit'; import authReducer from '../features/auth/authSlice'; import postsReducer from '../features/posts/postsSlice'; import { apiSlice } from '../services/api'; export const store = configureStore({ reducer: { auth: authReducer, posts: postsReducer, [apiSlice.reducerPath]: apiSlice.reducer }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(apiSlice.middleware) }); // Export types for TypeScript export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch;
// features/posts/postsSlice.js - Feature Slice import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit'; // Entity adapter for normalization const postsAdapter = createEntityAdapter({ sortComparer: (a, b) => b.date.localeCompare(a.date) }); // Async thunk export const fetchPosts = createAsyncThunk( 'posts/fetchPosts', async () => { const response = await fetch('/api/posts'); return await response.json(); } ); // Slice const postsSlice = createSlice({ name: 'posts', initialState: postsAdapter.getInitialState({ status: 'idle', error: null }), reducers: { postAdded: postsAdapter.addOne, postUpdated: postsAdapter.updateOne, postDeleted: postsAdapter.removeOne }, extraReducers: (builder) => { builder .addCase(fetchPosts.pending, (state) => { state.status = 'loading'; }) .addCase(fetchPosts.fulfilled, (state, action) => { state.status = 'succeeded'; postsAdapter.setAll(state, action.payload); }) .addCase(fetchPosts.rejected, (state, action) => { state.status = 'failed'; state.error = action.error.message; }); } }); export const { postAdded, postUpdated, postDeleted } = postsSlice.actions; export default postsSlice.reducer; // Export selectors export const { selectAll: selectAllPosts, selectById: selectPostById } = postsAdapter.getSelectors((state) => state.posts);
Feature-based structure mein har feature self-contained hota hai. Naya developer easily samajh sakta hai ki kaunsa code kahan hai. Scaling bhi easy ho jati hai!

9️⃣ Redux Toolkit Best Practices

1. ✅ Use createSlice

Manually actions/reducers mat banao

// ✅ Good const slice = createSlice({...}); // ❌ Avoid const INCREMENT = 'INCREMENT';

2. ✅ Normalize Complex State

createEntityAdapter use karo

// ✅ Good const adapter = createEntityAdapter(); // ❌ Avoid nested arrays

3. ✅ Use RTK Query

API calls ke liye RTK Query prefer karo

// ✅ Good const { data } = useGetPostsQuery(); // ❌ Manual thunks

4. ✅ Colocate Logic

Feature-based structure use karo

// ✅ Good features/posts/ postsSlice.js // ❌ Type-based actions/posts.js reducers/posts.js

5. ✅ Use Selectors

Reusable selectors banao

// ✅ Good const selectUser = (state) => state.user; // Use with useSelector

6. ✅ TypeScript Support

Types define karo better DX ke liye

// ✅ Good type RootState = ReturnType<typeof store.getState>;
⚠️ Common Mistakes to Avoid:
  • Purane Redux patterns use karna (action types, manual reducers)
  • State ko directly mutate karna outside reducers
  • Har choti cheez Redux mein dalna (local state use karo jahan possible)
  • Normalized state ko ignore karna large datasets ke liye
  • DevTools ko production mein enable rakhna

🎯 Interview Questions (Basic to Advanced)

Click on each question to reveal the answer

Q1. Redux Toolkit kya hai aur purane Redux se kaise different hai? (Basic) ▼

Answer:

Redux Toolkit (RTK): Official, opinionated toolset for efficient Redux development. Ye Redux ko use karna simple aur less verbose bana deta hai.

Key Differences:

Feature Old Redux Redux Toolkit
Boilerplate Bahut zyada Minimal
Actions Manual define Auto-generated
Immutability Manual (...spread) Immer (mutable syntax)
DevTools Manual setup Auto-configured

Hinglish: RTK ne Redux ko modern aur developer-friendly bana diya. Pehle 50 lines mein jo kaam hota tha, ab 15 lines mein ho jata hai!

Q2. createSlice kaise kaam karta hai? Iske main parts explain karo (Basic) ▼

Answer:

createSlice: Ek function jo slice of Redux state create karta hai with reducers and actions.

Main Parts:

const mySlice = createSlice({ // 1. Name - Slice ka unique naam name: 'counter', // 2. Initial State - Starting state initialState: { value: 0 }, // 3. Reducers - State update logic reducers: { increment(state) { state.value += 1; // Immer magic! }, incrementByAmount(state, action) { state.value += action.payload; } }, // 4. Extra Reducers - Handle external actions extraReducers: (builder) => { builder.addCase(someAsyncThunk.fulfilled, (state, action) => { state.data = action.payload; }); } });

Auto-Generated:

  • Actions: mySlice.actions.increment, mySlice.actions.incrementByAmount
  • Action Types: "counter/increment", "counter/incrementByAmount"
  • Reducer: mySlice.reducer

Hinglish: createSlice ek all-in-one solution hai. Aap sirf reducers define karo, baaki sab (actions, types) automatically ban jata hai!

Q3. createAsyncThunk kya hai aur kab use karte hain? Lifecycle actions explain karo (Intermediate) ▼

Answer:

createAsyncThunk: Async operations (API calls) handle karne ke liye utility. Ye automatically promise lifecycle actions generate karta hai.

Three Lifecycle Actions:

  1. pending: Jab async operation start hoti hai
  2. fulfilled: Jab successfully complete ho jaye
  3. rejected: Jab error aaye
// Create async thunk export const fetchUser = createAsyncThunk( 'users/fetchUser', async (userId, { rejectWithValue }) => { try { const response = await fetch(`/api/users/${userId}`); return await response.json(); } catch (error) { return rejectWithValue(error.message); } } ); // Handle in slice extraReducers: (builder) => { builder .addCase(fetchUser.pending, (state) => { state.loading = true; state.error = null; }) .addCase(fetchUser.fulfilled, (state, action) => { state.loading = false; state.user = action.payload; }) .addCase(fetchUser.rejected, (state, action) => { state.loading = false; state.error = action.payload; }); }

When to Use:

  • API calls (GET, POST, PUT, DELETE)
  • File uploads
  • Any async operation that needs Redux state

Hinglish: Jab bhi API call karni ho, createAsyncThunk use karo. Ye automatically loading, success, error states manage kar leta hai!

Q4. RTK Query kya hai? Traditional async thunks se kaise better hai? (Intermediate) ▼

Answer:

RTK Query: Powerful data fetching and caching tool built into Redux Toolkit. Ye complete backend integration solution hai.

RTK Query vs Async Thunks:

Feature Async Thunks RTK Query
Caching Manual Automatic
Loading States Manual Auto-generated
Refetching Manual Automatic
Deduplication No Yes
Polling Manual Built-in

RTK Query Benefits:

  • Auto Caching: Same data dobara fetch nahi hota
  • Cache Invalidation: Tags se automatic refetch
  • Optimistic Updates: UI instantly update, rollback on error
  • Polling: Auto refresh at intervals
  • Prefetching: Data pehle se load karo

Hinglish: RTK Query ek complete package hai API handling ke liye. Async thunks mein aapko sab manually karna padta hai, RTK Query mein sab automatic hai!

Q5. State normalization kya hai? createEntityAdapter kaise use karte hain? (Advanced) ▼

Answer:

State Normalization: Data ko flat structure mein store karna with IDs as keys, instead of nested arrays/objects.

Why Normalize?

  • Performance: O(1) lookups by ID
  • No Duplication: Same data multiple places mein nahi
  • Easy Updates: Direct ID se update karo
  • Consistency: Single source of truth
// createEntityAdapter Example import { createEntityAdapter } from '@reduxjs/toolkit'; // 1. Create adapter const usersAdapter = createEntityAdapter({ selectId: (user) => user.userId, // Custom ID field sortComparer: (a, b) => a.name.localeCompare(b.name) // Sort }); // 2. Get initial state const initialState = usersAdapter.getInitialState({ loading: false }); // Result: { ids: [], entities: {}, loading: false } // 3. Use adapter methods in reducers reducers: { userAdded: usersAdapter.addOne, usersReceived: usersAdapter.setAll, userUpdated: usersAdapter.updateOne, userRemoved: usersAdapter.removeOne } // 4. Export selectors export const { selectAll: selectAllUsers, selectById: selectUserById, selectIds: selectUserIds } = usersAdapter.getSelectors((state) => state.users); // 5. Use in component const users = useSelector(selectAllUsers); const user = useSelector(state => selectUserById(state, 5));

Adapter Methods:

  • addOne/addMany: Add entities
  • setOne/setAll: Replace entities
  • updateOne/updateMany: Update entities
  • upsertOne/upsertMany: Add or update
  • removeOne/removeMany/removeAll: Delete entities

Hinglish: Normalized state database ki tarah hai - har entity ka apna table (entities object) aur IDs array. Updates fast aur easy ho jate hain!

Q6. Immer.js kaise kaam karta hai RTK mein? Kya limitations hain? (Advanced) ▼

Answer:

Immer.js: Library jo "draft" state provide karti hai. Aap draft ko mutate kar sakte ho, Immer automatically immutable updates produce karta hai.

How it Works:

  1. Immer creates a "draft" copy of state (Proxy object)
  2. You mutate the draft directly
  3. Immer tracks all changes
  4. Produces new immutable state based on changes
// Without Immer (Old way) return { ...state, user: { ...state.user, profile: { ...state.user.profile, name: action.payload } } }; // With Immer (RTK way) state.user.profile.name = action.payload; // Simple! // Array operations state.todos.push(newTodo); // ✅ Works! state.todos.splice(index, 1); // ✅ Works! state.items.sort((a, b) => a - b); // ✅ Works!

Important Rules:

  • ❌ Don't mix: Either mutate draft OR return new state, not both
  • ❌ Don't return undefined: Return nothing or new state
  • ✅ Can return: Completely new state to replace
// ❌ Wrong - Mixing both state.value += 1; return { ...state }; // Don't do this! // ✅ Correct - Mutate only state.value += 1; // ✅ Correct - Return only return { value: state.value + 1 }; // ✅ Correct - Replace entire state return initialState;

Limitations:

  • Only works in reducers (not in thunks or components)
  • Can't use with non-serializable data (functions, Promises)
  • Performance overhead for very large states

Hinglish: Immer magic ki tarah hai - aap mutable code likhte ho, but output immutable hota hai. Ye Proxy objects use karta hai changes track karne ke liye!

Q7. Enterprise-level Redux app ka ideal folder structure kya hona chahiye? Best practices explain karo (Expert) ▼

Answer:

Feature-Based Structure (Recommended): Code ko features/domains ke basis pe organize karo, not by type.

src/ ├── app/ │ ├── store.ts // Store configuration │ ├── rootReducer.ts // Combine all reducers │ └── hooks.ts // Typed hooks (useAppDispatch, useAppSelector) │ ├── features/ │ ├── auth/ │ │ ├── authSlice.ts // Slice (state + reducers + actions) │ │ ├── authThunks.ts // Async thunks │ │ ├── authSelectors.ts // Memoized selectors │ │ ├── authAPI.ts // API calls │ │ ├── authTypes.ts // TypeScript types │ │ └── components/ // Feature-specific components │ │ ├── Login.tsx │ │ └── Register.tsx │ │ │ ├── posts/ │ │ ├── postsSlice.ts │ │ ├── postsSelectors.ts │ │ └── components/ │ │ │ └── users/ │ ├── usersSlice.ts │ └── usersAPI.ts │ ├── services/ // RTK Query APIs │ ├── baseApi.ts // Base API configuration │ ├── postsApi.ts // Posts endpoints │ └── usersApi.ts // Users endpoints │ ├── shared/ // Shared utilities │ ├── components/ // Reusable components │ ├── hooks/ // Custom hooks │ ├── utils/ // Helper functions │ └── constants/ // App constants │ └── types/ // Global TypeScript types └── index.ts

Best Practices:

  1. Feature Slices: Har feature ki apni slice, self-contained
  2. Colocate: Related code ek saath rakho
  3. Barrel Exports: index.ts files se clean imports
  4. Typed Hooks: useAppDispatch, useAppSelector banao
  5. Selectors: Reselect use karo memoization ke liye
  6. RTK Query: API logic separate services folder mein
// app/hooks.ts - Typed Hooks import { useDispatch, useSelector } from 'react-redux'; import type { RootState, AppDispatch } from './store'; export const useAppDispatch = () => useDispatch<AppDispatch>(); export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; // features/posts/index.ts - Barrel Export export { default as postsReducer } from './postsSlice'; export * from './postsSlice'; export * from './postsSelectors'; export * from './postsThunks';

Why Feature-Based?

  • Easy to find related code
  • Better for team collaboration
  • Easier to add/remove features
  • Scales well with app growth
  • Clear separation of concerns

Hinglish: Feature-based structure mein har feature ek mini-app ki tarah hai. Naya developer easily samajh sakta hai code kahan hai. Type-based structure (actions/, reducers/) mein sab scattered ho jata hai!

📝 Quick Summary

  • ✅ Redux Toolkit: Modern, less boilerplate, developer-friendly Redux
  • ✅ createSlice: All-in-one solution for actions + reducers
  • ✅ configureStore: One-line store setup with DevTools
  • ✅ createAsyncThunk: Easy async operations with lifecycle actions
  • ✅ RTK Query: Complete backend integration with auto caching
  • ✅ createEntityAdapter: Normalized state for performance
  • ✅ Immer.js: Mutable syntax, immutable updates
  • ✅ DevTools: Time-travel debugging built-in
  • ✅ Feature-Based: Organize by features, not types