COMPLETE REACT COURSE  •  PART 4 of 5

Building Real Apps

Organise complex state, share it across the app, add multiple pages and make everything fast: the architecture skills companies hire for.

4modules
40practice examples
~19hclass time
100%live code
Keys: → next   ← back   M menu   L Code Lab   F fullscreen
M13
useReducer & Context API
Reducers, global data without prop drilling
M14
React Router
Pages, params, layouts, protected routes
M15
Redux Toolkit
Slices, store, thunks, selectors
M16
Performance Optimization
memo, useMemo, useCallback, lazy, virtualization
BrainBit Infotech, Ranchi  |  Trainer: Nandan Kumar
13
MODULE 13  •  PART 4

useReducer & Context API

As apps grow, state gets complicated and must be shared far and wide. useReducer organises complex updates; Context delivers data to any component without prop drilling.

  • When useState gets messy
  • Reducers: (state, action) → new state
  • dispatch, action type & payload (animated)
  • The prop drilling problem
  • createContext, Provider, useContext
  • Context + reducer = a mini global store
  • Custom hooks for context
  • Performance & splitting contexts
YOU WILL BUILD:Theme switcherHindi/English toggleAuth login/logoutToast notificationsGlobal cartExpense tracker
🏦
App.jsx
function reducer(state, action) {
  switch (action.type) {
    case 'deposit':
      return state + action.amount;
    default: return state;
  }
}
10examples
● 5 Easy
● 3 Medium
● 2 Hard
each with live code
Complete React Course  |  BrainBit Infotech, Ranchi
M13Motivation

When useState gets messy

Messy.jsx
// 6 setters scattered across many handlers...
function addItem(p) {
  setItems([...items, p]);
  setCount(count + 1);
  setTotal(total + p.price);
  setLastAction('add');
  setError(null);
  setSaved(false);
}
// ...and removeItem, clearCart, applyCoupon all
// repeat similar logic. Easy to forget one!
🧹

useReducer

Puts ALL the update rules in one function called a reducer. Components just say WHAT happened: dispatch({{ type: 'add', item }}).
🔍

Easy to test & debug

Every change goes through one place. You can log each action and see exactly how state changed.
⚖️

Rule of thumb

Use useState for simple independent values. Use useReducer when the next state depends on the previous one in many different ways.
Complete React Course  |  Part 4  |  useReducer & Context API
M13Animated explainer

How a reducer works (the bank passbook)

App.jsx
function reducer(balance, action) {
  switch (action.type) {
    case 'deposit':  return balance + action.amount;
    case 'withdraw': return balance - action.amount;
    default:         return balance;
  }
}

const [balance, dispatch] = useReducer(reducer, 1000);

<button onClick={() =>
  dispatch({ type: 'deposit', amount: 500 })}>
  Deposit 500
</button>
🖱 Click "Deposit 500"
↓
dispatch(action){ type: 'deposit', amount: 500 } ← the deposit slip
↓
reducer(1000, action)the bank clerk follows the rules
↓
new balance = 1000passbook updated → re-render
Complete React Course  |  Part 4  |  useReducer & Context API
M13Live demo

useReducer live

const [state, dispatch] = useReducer(reducer, initialState). The reducer lives outside the component (it is a pure function). Every action is logged so you can see the flow.
App.jsx
LIVELive Preview
Console
✍ Try it Withdraw until the balance is below 300: the reducer refuses. Add a "Deposit 2000" button.
Complete React Course  |  Part 4  |  useReducer & Context API
M13Animated explainer

Prop drilling vs Context

❌ Prop drilling

<App theme="dark" />
<Layout theme={...} />
<Sidebar theme={...} />
<Menu theme={...} />
<Button />
3 components pass a prop they never use

✅ Context

<ThemeContext.Provider value="dark">
<Layout />
<Sidebar />
<Menu />
<Button />useContext(ThemeContext) → "dark"
Complete React Course  |  Part 4  |  useReducer & Context API
M13Live demo

Context in 3 steps

1. createContext(defaultValue)   2. wrap components in <MyContext.Provider value={...}>   3. read it anywhere below with useContext(MyContext). When the Provider value changes, every component using it re-renders.
App.jsx
LIVELive Preview
Console
✍ Try it Notice that Toolbar passes no props, yet Button knows the theme. Add a third Button inside Toolbar.
Complete React Course  |  Part 4  |  useReducer & Context API
M13Best practice

Professional pattern: Provider component + custom hook

AuthContext.jsx
// AuthContext.jsx
import { createContext, useContext, useState } from 'react';

const AuthContext = createContext(null);

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const login = (name) => setUser({ name });
  const logout = () => setUser(null);
  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error('useAuth must be inside <AuthProvider>');
  return ctx;
}
usage
// main.jsx
<AuthProvider>
  <App />
</AuthProvider>

// Navbar.jsx (anywhere deep inside)
const { user, logout } = useAuth();
📦

Provider component

Hides the state and functions inside one file. The rest of the app just uses them.
🪝

Custom hook

useAuth() is shorter than useContext(AuthContext) and gives a clear error if you forget the Provider.
Performance tipEvery consumer re-renders when the value changes. Split big contexts (ThemeContext, AuthContext, CartContext) instead of one giant one.
Complete React Course  |  Part 4  |  useReducer & Context API
M13Practice 1 of 10  •  Easy

Example 1: Counter with useReducer

● Easy💼 Real project: Learning the reducer pattern
The simplest reducer: actions increment, decrement and reset. The component only dispatches; all logic is in the reducer.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add an action { type: 'add', amount: 10 } and a "+10" button.
case 'add': return { count: state.count + action.amount };
Complete React Course  |  Part 4  |  useReducer & Context API
M13Practice 2 of 10  •  Easy

Example 2: Theme Context for the Whole App

● Easy💼 Real project: Dark mode across every page
A ThemeProvider component owns the theme and a toggle function. A deep Card and the Header read it with a useTheme() hook.
App.jsxThemeContext.jsx
LIVELive Preview
Console
✍ Try it yourself Add a "green" theme palette and make the toggle cycle through all three.
Add green: { bg: '#ebfbee', ... } and cycle with an array of names.
Complete React Course  |  Part 4  |  useReducer & Context API
M13Practice 3 of 10  •  Easy

Example 3: English / Hindi Language Switch

● Easy💼 Real project: Multilingual government & school sites
A LanguageContext provides the current language and a translate function t(key). Every component shows text in the chosen language. (Big apps use libraries like react-i18next the same way.)
App.jsxLanguageContext.jsx
LIVELive Preview
Console
✍ Try it yourself Add a new word "contact" in both languages and show it in the Navbar.
English: Contact, Hindi: संपर्क
Complete React Course  |  Part 4  |  useReducer & Context API
M13Practice 4 of 10  •  Easy

Example 4: Logged-In User Context

● Easy💼 Real project: Showing the user name in header, sidebar, profile
The user object is provided once at the top. Navbar, Sidebar and ProfileCard all read it directly, without passing it through Layout.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Change the user object in App: all three components update. Add an Avatar component that shows the first letter of the name.
Read the context in Avatar with useContext(UserContext).
Complete React Course  |  Part 4  |  useReducer & Context API
M13Practice 5 of 10  •  Easy

Example 5: Form State with a Reducer

● Easy💼 Real project: Large forms with many fields
One SET_FIELD action updates any field using action.field and action.value. A RESET action clears everything. Very handy for forms with 10+ fields.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a FILL_DEMO action that fills all fields with sample data (useful for testing).
case 'FILL_DEMO': return { name: 'Test', phone: '9876543210', course: 'Java', notes: 'demo' };
Complete React Course  |  Part 4  |  useReducer & Context API
M13Practice 6 of 10  •  Medium

Example 6: Todo App with useReducer

●● Medium💼 Real project: Task managers
Compare with Module 6: same app, but every change is an action (added, toggled, deleted, cleared). The reducer is easy to read, test and extend.
App.jsxtodosReducer.js
LIVELive Preview
Console
✍ Try it yourself Add an edited action that changes a todo text (double-click to edit with prompt()).
case 'edited': return todos.map(t => t.id === action.id ? { ...t, text: action.text } : t);
Complete React Course  |  Part 4  |  useReducer & Context API
M13Practice 7 of 10  •  Medium

Example 7: Auth Context: Login / Logout

●● Medium💼 Real project: Every app with user accounts
An AuthProvider exposes user, login and logout. The Navbar and a protected Dashboard react to the login state. A guard in useAuth catches missing Providers.
App.jsxAuthContext.jsx
LIVELive Preview
Console
✍ Try it yourself Save the user in localStorage so a refresh keeps you logged in.
Use the useLocalStorage hook from Module 12 instead of useState inside AuthProvider.
Complete React Course  |  Part 4  |  useReducer & Context API
M13Practice 8 of 10  •  Medium

Example 8: Toast Notification System

●● Medium💼 Real project: Success/error popups in every app
Any component can call notify('Saved!', 'success') through context. The ToastProvider keeps a list of toasts and removes each one after 3 seconds.
App.jsxToastContext.jsx
LIVELive Preview
Console
✍ Try it yourself Click the buttons quickly: toasts stack and disappear one by one. Add a "warning" type in orange.
Add warning: '#f08c00' to colors and a button that uses it.
Complete React Course  |  Part 4  |  useReducer & Context API
M13Practice 9 of 10  •  Hard

Example 9: Global Cart: Context + useReducer

●●● Hard💼 Real project: E-commerce (Amazon-style cart everywhere)
The classic professional setup: a reducer holds the cart rules, a context shares state and dispatch, and a useCart() hook gives any component access. Header, product list and cart page all stay in sync.
App.jsxCartContext.jsx
LIVELive Preview
Console
✍ Try it yourself Add a "Clear cart" button in CartPanel using the existing CLEAR action.
<button onClick={() => dispatch({ type: 'CLEAR' })}>Clear</button>
Complete React Course  |  Part 4  |  useReducer & Context API
M13Practice 10 of 10  •  Hard

Example 10: Expense Tracker Store

●●● Hard💼 Real project: Personal finance apps (Walnut, Money Manager)
A full mini app: ExpenseContext + reducer with ADD and DELETE, a form, a list, and a summary with income, expense, balance and a category bar chart, all derived from one transactions array.
App.jsxExpenseContext.jsxApp.css
LIVELive Preview
Console
✍ Try it yourself Add an expense of -300 for "Food" and watch the chart bar grow. Then persist the list in localStorage.
useReducer accepts a 3rd argument (an init function) to load from localStorage; save with an effect.
Complete React Course  |  Part 4  |  useReducer & Context API
M13Check your understanding

Quick Quiz

Click an answer. Wrong answers shake, the right one turns green and explains why.

Q1. A reducer function receives...
✔ reducer(state, action) => newState, and it must not modify the old state.
Q2. What problem does Context solve?
✔ Any component inside the Provider can read the value directly.
Q3. Where can useContext(ThemeContext) read the Provider value?
✔ Outside the Provider you get the default value from createContext.
🎯
Your score: 0 / 3 (first try counts)
Answer all 3 questions. Discuss any wrong answer with your partner before moving on.
Complete React Course  |  Part 4  |  useReducer & Context API
M13Module summary

Recap: What you learned

  • ✔useReducer(reducer, initial) → [state, dispatch].
  • ✔Actions describe what happened: { type, ...payload }; reducers decide the new state.
  • ✔Context = createContext + Provider + useContext.
  • ✔Wrap context in a Provider component and a custom hook (useAuth, useCart).
  • ✔Context + useReducer = a simple global store.
  • ✔Split contexts to avoid unnecessary re-renders.
🏆

Module 13 complete!

Finish all 10 examples and the "Try it yourself" tasks before moving on.

Practice folder: D:\react-practice\m13

➡ Next up

Module 14: React Router. Build multi-page apps with URLs.
Complete React Course  |  Part 4  |  useReducer & Context API
14
MODULE 14  •  PART 4

React Router

Real apps have many pages: Home, Courses, Course details, Cart, Dashboard. React Router maps URLs to components, without ever reloading the page.

  • How SPA routing works (animated)
  • BrowserRouter, Routes, Route
  • Link vs <a> (no page reload)
  • NavLink for active menus
  • URL parameters: /courses/:id
  • Nested routes & Outlet layouts
  • useNavigate & useSearchParams
  • 404 pages & protected routes
YOU WILL BUILD:Multi-page siteActive navbarCourse details pageDashboard layoutLogin-protected pagesMini e-commerce store
🧭
App.jsx
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/courses/:id"
         element={<CourseDetail />} />
  <Route path="*" element={<NotFound />} />
</Routes>
10examples
● 5 Easy
● 3 Medium
● 2 Hard
each with live code
Complete React Course  |  BrainBit Infotech, Ranchi
M14Animated explainer

How routing works in a Single Page App

https://myapp.in/
Home · Courses · About
🏠 Home page
Navbar never reloads. Only this area changes.
URLComponent shown
/<Home />
/courses<Courses />
/courses/:id<CourseDetail /> (id = "2")
* (anything else)<NotFound />
Key ideaReact Router watches the address bar. When it changes, it picks the matching Route and renders its element. The server is not asked for a new page.
Complete React Course  |  Part 4  |  React Router
M14Getting started

Setup: install and wrap your app

D:\react-practice> npm install react-router-dom
added 3 packages in 4s
main.jsx
// main.jsx
import { BrowserRouter } from 'react-router-dom';

createRoot(document.getElementById('root')).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);
App.jsx
// App.jsx
import { Routes, Route, Link } from 'react-router-dom';

export default function App() {
  return (
    <>
      <nav>
        <Link to="/">Home</Link> | <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </>
  );
}
In real projectsIn our slides each preview has a fake address bar so you can see the URL change. Click the URL to type your own!
Complete React Course  |  Part 4  |  React Router
M14Live demo

Your first routes (with a fake address bar)

Routes looks at the URL and renders the FIRST matching Route. Link changes the URL without reloading. Try the back/forward buttons in the preview's address bar too.
App.jsx
LIVELive Preview
Console
✍ Try it Add a /gallery page with a Link and a Route. Then type /contact in the fake address bar.
Complete React Course  |  Part 4  |  React Router
M14Important difference

Link vs <a href>

❌ <a href="/about">

The browser asks the server for a brand-new page. The whole app restarts: all state is lost (cart, form, login), and you see a white flash.
App.jsx
<a href="/about">About</a>
VS

✅ <Link to="/about">

React Router changes the URL using the History API and swaps the component. Instant, state is kept, back button still works.
App.jsx
<Link to="/about">About</Link>
🔗

External sites

Use a normal <a href="https://..."> for links to OTHER websites.
🎯

NavLink

Like Link, but knows if it is active: className={{({{ isActive }}) => isActive ? 'on' : ''}}.
🧭

useNavigate

Go to a page from code: after login, after saving a form: navigate('/dashboard').
Complete React Course  |  Part 4  |  React Router
M14Live demo

URL parameters with useParams

A route path like /courses/:id matches /courses/1, /courses/2... Inside the component, useParams() returns { id: '2' } (always a string). One component serves every course page!
App.jsx
LIVELive Preview
Console
✍ Try it Type /courses/3 and then /courses/99 in the address bar.
Complete React Course  |  Part 4  |  React Router
M14Animated explainer

Nested routes & Outlet (shared layouts)

App.jsx
<Routes>
  <Route path="/dashboard" element={<DashboardLayout />}>
    <Route index element={<Overview />} />
    <Route path="profile" element={<Profile />} />
    <Route path="fees" element={<Fees />} />
  </Route>
</Routes>

function DashboardLayout() {
  return (
    <div className="layout">
      <Sidebar />
      <main><Outlet /></main>   {/* child goes here */}
    </div>
  );
}
myapp.in/dashboard
Sidebar
▸ Overview
▸ Profile
▸ Fees
<Outlet />  
Complete React Course  |  Part 4  |  React Router
M14Power tools

useNavigate, useSearchParams & protected routes

🧭 useNavigate: go from code

App.jsx
const navigate = useNavigate();

function handleLogin() {
  // ...check password
  navigate('/dashboard');
}
navigate(-1); // like Back

🔍 useSearchParams: ?query

App.jsx
// URL: /shop?category=Art&sort=price
const [params, setParams] =
  useSearchParams();
const cat = params.get('category');

setParams({ category: 'Home' });

Filters in the URL can be bookmarked and shared!

🔒 Protected route

App.jsx
function RequireAuth({ children }) {
  const { user } = useAuth();
  if (!user)
    return <Navigate to="/login" replace />;
  return children;
}

<Route path="/dashboard" element={
  <RequireAuth><Dashboard /></RequireAuth>
} />
In real projectsReact Router v7 (2025) also offers data routers (createBrowserRouter with loaders and actions) and a full framework mode. Everything you learn here works in v6 and v7.
Complete React Course  |  Part 4  |  React Router
M14Practice 1 of 10  •  Easy

Example 1: Home & About Pages

● Easy💼 Real project: Any multi-page website
The minimum router app: two components, two routes, two links. Use the fake address bar's back button to see browser history working.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a Facilities page at /facilities listing Library, Lab and Sports.
New component, new Link, new Route.
Complete React Course  |  Part 4  |  React Router
M14Practice 2 of 10  •  Easy

Example 2: NavLink Active Menu

● Easy💼 Real project: Navigation bars that show the current page
NavLink gives isActive to its className or style function, so the current page is highlighted automatically. No state needed!
App.jsxApp.css
LIVELive Preview
Console
✍ Try it yourself Remove the end prop and visit /courses. Why is Home also active now?
Without end, "/" matches the start of every URL. end means "match exactly".
Complete React Course  |  Part 4  |  React Router
M14Practice 3 of 10  •  Easy

Example 3: 404 Not Found Page

● Easy💼 Real project: Every production website
A route with path="*" catches every URL that did not match. Always put it last. A good 404 page links back home.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Type any random URL like /abc/xyz in the address bar.
Anything that does not match shows NotFound.
Complete React Course  |  Part 4  |  React Router
M14Practice 4 of 10  •  Easy

Example 4: Course Detail Page (/courses/:id)

● Easy💼 Real project: Product, blog post and profile pages
The list links to /courses/react, /courses/python... One CourseDetail component reads the :slug parameter and finds the right course.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a "Next course →" link at the bottom of the detail page.
Find the index of the current course and link to courses[(i + 1) % courses.length].slug.
Complete React Course  |  Part 4  |  React Router
M14Practice 5 of 10  •  Easy

Example 5: Redirect after Form Submit

● Easy💼 Real project: Sign-up and checkout flows
After submitting, call navigate('/thank-you', { state: { name } }). The thank-you page reads that data with useLocation().state.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Use navigate('/thank-you', { replace: true, state }) so the Back button does not return to the form.
replace swaps the current history entry instead of adding a new one.
Complete React Course  |  Part 4  |  React Router
M14Practice 6 of 10  •  Medium

Example 6: Dashboard with Nested Routes

●● Medium💼 Real project: Admin panels, student portals
A parent route renders DashboardLayout (sidebar + <Outlet />). Child routes fill the outlet. The index route is the default child.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a "Certificates" child route at /dashboard/certificates.
A new NavLink in the sidebar and a new child Route inside the dashboard route.
Complete React Course  |  Part 4  |  React Router
M14Practice 7 of 10  •  Medium

Example 7: Filters in the URL (useSearchParams)

●● Medium💼 Real project: Shopping filters you can bookmark and share
The category and sort live in the URL query string (?category=Art&sort=price) instead of state. Copy the URL, open it later, and the same filters appear.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Click filters and watch the address bar. Then type /?category=Home&sort=price directly.
The page reads everything from the URL, so typing it gives the same result.
Complete React Course  |  Part 4  |  React Router
M14Practice 8 of 10  •  Medium

Example 8: Breadcrumbs from the URL

●● Medium💼 Real project: E-commerce and documentation sites
useLocation() gives the current path. We split it into parts and build clickable breadcrumbs: Home › Shop › Art › Sohrai.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Navigate to "Sohrai Painting", then click "Art" in the breadcrumbs.
Every crumb is a Link built from the path pieces before it.
Complete React Course  |  Part 4  |  React Router
M14Practice 9 of 10  •  Hard

Example 9: Protected Dashboard (Login Required)

●●● Hard💼 Real project: Banking, school portals, admin areas
A RequireAuth wrapper checks the auth context. Not logged in? It redirects to /login with <Navigate /> and remembers where you wanted to go, then sends you back after login.
App.jsx
LIVELive Preview
Console
✍ Try it yourself As a guest, click Reports: you go to login, then back to Reports after logging in. Add an admin-only page.
Store a role in the user and make a RequireAdmin wrapper that checks user.role === 'admin'.
Complete React Course  |  Part 4  |  React Router
M14Practice 10 of 10  •  Hard

Example 10: Mini E-commerce Store

●●● Hard💼 Real project: Online shops (list → detail → cart → checkout)
Everything together: routes for home, product details (:id), cart and order success; a cart context shared across pages; useNavigate after checkout; a cart badge in the header.
App.jsxpages.jsxcart.jsx
LIVELive Preview
Console
✍ Try it yourself Add a /checkout page with a name/address form between Cart and Success.
Cart button navigates to /checkout; the checkout form submit clears the cart and navigates to /success.
Complete React Course  |  Part 4  |  React Router
M14Check your understanding

Quick Quiz

Click an answer. Wrong answers shake, the right one turns green and explains why.

Q1. Why use <Link> instead of <a href> inside your app?
✔ Link uses client-side navigation: instant and state is preserved.
Q2. For the route /student/:roll, how do you read the roll number?
✔ useParams returns all URL parameters as strings.
Q3. In nested routes, where does the child page appear?
✔ Outlet is the placeholder for the matched child route.
🎯
Your score: 0 / 3 (first try counts)
Answer all 3 questions. Discuss any wrong answer with your partner before moving on.
Complete React Course  |  Part 4  |  React Router
M14Module summary

Recap: What you learned

  • ✔Wrap the app in <BrowserRouter>; define <Routes> and <Route path element>.
  • ✔Use Link/NavLink for internal navigation, never <a>.
  • ✔:id params → useParams(); query strings → useSearchParams().
  • ✔Nested routes + <Outlet /> = shared layouts; index = default child.
  • ✔useNavigate() to move from code; <Navigate /> to redirect.
  • ✔path="*" for 404; wrapper components for protected routes.
🏆

Module 14 complete!

Finish all 10 examples and the "Try it yourself" tasks before moving on.

Practice folder: D:\react-practice\m14

➡ Next up

Module 15: Redux Toolkit. Professional global state for large apps.
Complete React Course  |  Part 4  |  React Router
15
MODULE 15  •  PART 4

Redux Toolkit (Global State)

Big apps (e-commerce, ERPs, dashboards) keep shared data in one central store. Redux Toolkit is the official, modern way to do that, and it is asked about in many React job interviews.

  • Why a global store?
  • The Redux flow (animated)
  • Slices: state + reducers + actions
  • configureStore and Provider
  • useSelector and useDispatch
  • Async data with createAsyncThunk
  • Selectors & createSelector
  • Redux vs Context vs Zustand
YOU WILL BUILD:Counter & theme slicesTodo sliceCart with totalsAsync product loadingAuth + cart storeAttendance stats app
🏢
App.jsx
const cartSlice = createSlice({
  name: 'cart',
  initialState: [],
  reducers: {
    add: (state, action) => {
      state.push(action.payload);
    },
  },
});
10examples
● 5 Easy
● 3 Medium
● 2 Hard
each with live code
Complete React Course  |  BrainBit Infotech, Ranchi
M15Animated explainer

The Redux flow (one-way data cycle)

🖱 UI componentuser clicks "Add to cart"
dispatch(action){ type: 'cart/add', payload: product }
Reducer (in a slice)calculates the new state
🏢 Storethe single source of truth
useSelector()components read new data → re-render
→
→
→
→
→
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15The modern setup

Redux Toolkit in 4 files

counterSlice.js
// 1. features/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1; },
    addBy: (state, action) => { state.value += action.payload; },
  },
});
export const { increment, addBy } = counterSlice.actions;
export default counterSlice.reducer;
store.js
// 2. store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './features/counterSlice';

export const store = configureStore({
  reducer: { counter: counterReducer },
});
main.jsx
// 3. main.jsx: give the store to the whole app
import { Provider } from 'react-redux';
import { store } from './store';

<Provider store={store}>
  <App />
</Provider>
Counter.jsx
// 4. Any component
import { useSelector, useDispatch } from 'react-redux';
import { increment, addBy } from './features/counterSlice';

const value = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
<button onClick={() => dispatch(addBy(5))}>+5</button>
Key ideastate.value += 1 looks like mutation, but Redux Toolkit uses a library called Immer that turns it into a safe, immutable update. This only works inside createSlice reducers!
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Live demo

Redux Toolkit live

Everything in one file for the demo (in real projects split it as on the previous slide). Two different components read and change the SAME store. Actions are logged with their auto-generated type.
App.jsx
LIVELive Preview
Console
✍ Try it Check the console: addBy(5) creates { type: "counter/addBy", payload: 5 }. Add a decrement reducer and button.
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Loading data into the store

Async data: createAsyncThunk

App.jsx
export const fetchProducts = createAsyncThunk(
  'products/fetch',
  async () => {
    const res = await fetch('/api/products');
    return res.json();          // becomes action.payload
  }
);

const productsSlice = createSlice({
  name: 'products',
  initialState: { items: [], status: 'idle', error: null },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchProducts.pending, (s) => { s.status = 'loading'; })
      .addCase(fetchProducts.fulfilled, (s, a) => {
        s.status = 'succeeded'; s.items = a.payload;
      })
      .addCase(fetchProducts.rejected, (s, a) => {
        s.status = 'failed'; s.error = a.error.message;
      });
  },
});

// in a component:
useEffect(() => { dispatch(fetchProducts()); }, [dispatch]);
⏳

pending

Dispatched automatically when the request starts: set status to "loading".
✅

fulfilled

When the async function returns: save action.payload.
❌

rejected

If it throws: save the error message.
⚡

RTK Query

Redux Toolkit also includes RTK Query, which generates data-fetching hooks with caching (similar to TanStack Query).
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Decision guide

Which state tool should I use?

ToolBest forExample
useStateLocal state of one componentIs this dropdown open? Input text
Lifting stateShared by a few nearby componentsFilters + results on the same page
Context (+ useReducer)Rarely-changing global valuesTheme, language, logged-in user
Redux ToolkitLarge apps, many features, team projects, DevTools time-travelE-commerce cart + orders + user + products
ZustandSimple global store with very little codeSmall-medium apps, game state
TanStack Query / RTK QueryServer data (cache of API responses)Product lists, user profiles
zustand-example.js
// Zustand: a whole store in 6 lines
import { create } from 'zustand';
const useCart = create((set) => ({
  items: [],
  add: (p) => set((s) => ({ items: [...s.items, p] })),
}));
const items = useCart((s) => s.items);
Key ideaDo not put EVERYTHING in Redux. Form inputs and UI toggles usually stay local. Server data is best in a query library. Redux is for shared client state.
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Practice 1 of 10  •  Easy

Example 1: Counter Slice

● Easy💼 Real project: Learning Redux step by step
Split like a real project: counterSlice.js, store.js and App.jsx. The slice auto-generates action creators from the reducer names.
App.jsxcounterSlice.jsstore.js
LIVELive Preview
Console
✍ Try it yourself Prevent the counter from going below 0 inside the decrement reducer.
if (state.value > 0) state.value -= 1;
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Practice 2 of 10  •  Easy

Example 2: Theme Slice

● Easy💼 Real project: Global UI settings
A tiny ui slice stores the theme and font size. Header and Page are separate components, but both read the same store.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a language field (en/hi) with a setLanguage reducer that takes a payload.
setLanguage: (s, a) => { s.language = a.payload; } and dispatch(setLanguage('hi')).
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Practice 3 of 10  •  Easy

Example 3: Todo Slice

● Easy💼 Real project: Task lists in a Redux app
Reducers that receive a payload: addTodo(text), toggleTodo(id), removeTodo(id). Thanks to Immer we can use push and find safely.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a clearDone reducer that removes all completed todos.
clearDone: (state) => state.filter(t => !t.done) (returning a new array also works).
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Practice 4 of 10  •  Easy

Example 4: Derived Data with Selectors

● Easy💼 Real project: Dashboards: counts, totals, percentages
Selectors are small functions that read or calculate from the store: selectDoneCount, selectProgress. Keep them next to the slice and reuse them everywhere.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a selectNextLesson selector that returns the first lesson not done, and show "Next: ..." under the bar.
(s) => s.lessons.find(l => !l.done)?.title || 'All done!'
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Practice 5 of 10  •  Easy

Example 5: Two Components, One Store

● Easy💼 Real project: Cart badge + product page
The ProductGrid dispatches; the CartBadge (far away in the header) selects. No props, no context wiring: both just talk to the store.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a CartList component in a sidebar that lists all item names from the store.
useSelector((s) => s.cart) and map to <li>.
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Practice 6 of 10  •  Medium

Example 6: Cart Slice with Quantities & Totals

●● Medium💼 Real project: Real e-commerce cart logic
A professional cart slice: add (or increase), decrease, remove, clear. Totals come from selectors. The UI shows a mini invoice with GST.
App.jsxcartSlice.jsstore.js
LIVELive Preview
Console
✍ Try it yourself Add a coupon field to the slice state with an applyCoupon reducer: code "RANCHI10" gives 10% off.
Store the discount percent in state and use it in the total selector.
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Practice 7 of 10  •  Medium

Example 7: Async Thunk: Load Products

●● Medium💼 Real project: Loading server data into Redux
createAsyncThunk wraps the fetch. The slice handles pending, fulfilled and rejected to track status. The component dispatches the thunk once on mount.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Watch the status change: idle → loading → succeeded. Make it fail by changing the URL to /api/error.
The rejected case stores the error message.
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Practice 8 of 10  •  Medium

Example 8: Auth + Cart: Multiple Slices

●● Medium💼 Real project: Real store shape with many features
A real store has many slices: auth and cart here. A component can select from both. Notice how logging out also clears the cart: the cart slice listens to the auth logout action using extraReducers.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Log in, add 3 items, log out: the cart empties. Log the whole store state with store.getState() in the console.
store.subscribe(() => console.log(store.getState())) logs after every action.
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Practice 9 of 10  •  Hard

Example 9: Filters with Memoized Selectors

●●● Hard💼 Real project: Big product catalogues
Filters live in a filters slice; products in a products slice. createSelector builds a memoized selector: the filtered list is only recalculated when products or filters change (the console shows when).
App.jsx
LIVELive Preview
Console
✍ Try it yourself Click "Toggle theme": the console does NOT show "recalculating" because the inputs to the selector did not change. Move the slider: it does.
createSelector caches the last result and returns it when its input selectors return the same values.
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Practice 10 of 10  •  Hard

Example 10: Student Attendance App with Stats

●●● Hard💼 Real project: School ERP attendance module
A students slice with markPresent, markAbsent and markAll. Selectors compute present/absent counts and percentage; an SVG donut chart and bar show the stats live.
App.jsxattendanceSlice.jsstore.jsDonut.jsx
LIVELive Preview
Console
✍ Try it yourself Add a "Late" status (L, orange) to the buttons, the selector and the donut.
Add 'L' to the button list, count it in selectStats, and add a third coloured segment.
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Check your understanding

Quick Quiz

Click an answer. Wrong answers shake, the right one turns green and explains why.

Q1. In Redux, how does a component change the store?
✔ Components dispatch actions; reducers compute the new state.
Q2. Why can you write state.value += 1 inside createSlice?
✔ Only inside createSlice/createReducer, thanks to Immer.
Q3. Best place for API data that many screens cache and refetch?
✔ Server-state libraries handle caching, refetching and loading states.
🎯
Your score: 0 / 3 (first try counts)
Answer all 3 questions. Discuss any wrong answer with your partner before moving on.
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
M15Module summary

Recap: What you learned

  • ✔Redux keeps shared state in ONE store; data flows in one direction.
  • ✔createSlice = name + initialState + reducers → actions generated for you.
  • ✔configureStore + <Provider store> make it available to the app.
  • ✔useSelector reads, useDispatch sends actions.
  • ✔createAsyncThunk handles pending/fulfilled/rejected for async work.
  • ✔Pick the simplest tool that works: local state → context → Redux/Zustand.
🏆

Module 15 complete!

Finish all 10 examples and the "Try it yourself" tasks before moving on.

Practice folder: D:\react-practice\m15

➡ Next up

Module 16: Performance Optimization. Make big React apps fast.
Complete React Course  |  Part 4  |  Redux Toolkit (Global State)
16
MODULE 16  •  PART 4

Performance Optimization

React is fast by default, but big apps with thousands of rows or heavy calculations can feel slow. Learn to measure, then fix with the right tool.

  • Why components re-render (animated)
  • Measure first: React DevTools Profiler
  • React.memo
  • useMemo for expensive calculations
  • useCallback for stable functions
  • Code splitting: lazy + Suspense
  • useTransition & useDeferredValue
  • Virtualizing huge lists
YOU WILL BUILD:Render counterMemoized listLazy-loaded chart2,000-row searchVirtual 10,000-row listOptimised dashboard
⚡
App.jsx
const ProductCard = memo(function ProductCard({ p }) {
  return <div>{p.name}</div>;
});

const sorted = useMemo(
  () => sortBig(list), [list]);
10examples
● 5 Easy
● 3 Medium
● 2 Hard
each with live code
Complete React Course  |  BrainBit Infotech, Ranchi
M16Animated explainer

Why do components re-render?

<App />search text changes: "_"
<SearchBar />needs search
<ProductList />1,000 cards!
<Footer />static
💥 All 3 children re-render, even Footer!
wrap Footer & ProductList in memo()skip render when props are the same
1

State changes

The component with the state re-renders.
2

Children follow

By default ALL its children re-render too, even if their props did not change.
3

Usually fine!

Re-rendering is cheap; React only touches the DOM if output changed. Optimise only when something is actually slow.
Complete React Course  |  Part 4  |  Performance Optimization
M16Live demo

See re-renders happen (render badges)

Each component shows how many times it has rendered; the badge flashes on every render. Type in the box: Parent, Child AND Footer all re-render, even though Footer receives no props at all.
App.jsxRenderCount.jsx
LIVELive Preview
Console
✍ Try it Type 5 letters and watch all badges grow. Next slide: stop Footer from re-rendering.
Complete React Course  |  Part 4  |  Performance Optimization
M16The 3 tools

React.memo, useMemo, useCallback

🛡️ React.memo(Component)

Skip re-rendering a component if its props are the same as last time (shallow compare).
App.jsx
const Footer = memo(function Footer() {
  return <footer>...</footer>;
});

🧮 useMemo(fn, deps)

Remember a calculated value and only recalculate when deps change. For slow work: sorting/filtering thousands of items.
App.jsx
const sorted = useMemo(
  () => [...list].sort(byPrice),
  [list]
);

🔗 useCallback(fn, deps)

Remember a function so it keeps the same identity between renders. Needed when passing functions to memo children.
App.jsx
const handleAdd = useCallback(
  (p) => setCart((c) => [...c, p]),
  []
);
Common mistakeThe trap: memo compares props with ===. A new object {{}}, array [] or arrow function () => {{}} created during render is a NEW reference every time, so memo always re-renders. Fix it with useMemo / useCallback.
In real projectsReact Compiler (stable since late 2025) can add this memoization automatically at build time. Learn the concepts anyway: you need them to understand and debug React apps.
Complete React Course  |  Part 4  |  Performance Optimization
M16Animated explainer

Code splitting: lazy() + Suspense

Without code splitting

app.js 2.4 MB (Home + Admin + Charts + Editor)
⏳ The user waits for EVERYTHING, even pages they never open.

With lazy() + Suspense

main.js 300 KB (Home) ✅ loaded first
Charts.js 600 KB: loaded when the user opens Reports
Admin.js 800 KB: only for admins
App.jsx
const Charts = lazy(() => import('./Charts'));

<Suspense fallback={<Spinner />}>
  {showReports && <Charts />}
</Suspense>
Complete React Course  |  Part 4  |  Performance Optimization
M16Concurrent features

Keep typing smooth: useTransition & useDeferredValue

useDeferredValue
// useDeferredValue: show the input instantly,
// let the slow list catch up a moment later
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);

<input value={query} onChange={(e) => setQuery(e.target.value)} />
<SlowList query={deferredQuery} />
useTransition
// useTransition: mark an update as "not urgent"
const [isPending, startTransition] = useTransition();

function selectTab(tab) {
  startTransition(() => setTab(tab));  // can be interrupted
}
{isPending && <Spinner />}
⌨️

Urgent updates

Typing, clicking, pressing: the user expects instant feedback.
🐴

Non-urgent updates

Filtering a huge list, switching to a heavy tab: a small delay is OK.
🧠

Concurrent React

React 18+ can pause a slow render to handle typing first, then continue. These two hooks tell React what can wait.
📊

Measure first!

Open React DevTools > Profiler, record, and look for components that take many milliseconds. Fix those, not everything.
Complete React Course  |  Part 4  |  Performance Optimization
M16Practice 1 of 10  •  Easy

Example 1: Render Counter: Who Re-rendered?

● Easy💼 Real project: Debugging performance problems
Three cards show their render counts. Clicking "Like" on one card re-renders the parent, so ALL cards re-render. This is the baseline we will optimise in the next example.
App.jsxRenderCount.jsx
LIVELive Preview
Console
✍ Try it yourself Like "React" 3 times. How many times did Python and Java render? (They did not need to!)
Each click re-renders App, and App re-renders every child.
Complete React Course  |  Part 4  |  Performance Optimization
M16Practice 2 of 10  •  Easy

Example 2: React.memo Stops Extra Renders

● Easy💼 Real project: Lists of cards, table rows, sidebars
Wrapping Footer and Sidebar in memo() makes them skip re-rendering when their props are unchanged. Type in the box: only Parent and Preview re-render now.
App.jsxRenderCount.jsx
LIVELive Preview
Console
✍ Try it yourself Change links="Home, Courses" to links={["Home", "Courses"].join(", ")}. Still memoized? Now try links={["Home"]} (an array). What happens and why?
A string is compared by value, but a new array is a new reference each render, so memo cannot skip.
Complete React Course  |  Part 4  |  Performance Optimization
M16Practice 3 of 10  •  Easy

Example 3: useMemo for a Filtered List

● Easy💼 Real project: Search over big datasets
Filtering 5,000 students on every render wastes time when only the theme changes. useMemo remembers the result and re-filters only when query changes. Watch the console timing.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Toggle the theme: no "filtered in" log. Remove useMemo (plain calculation) and toggle again: now it filters every time.
Replace useMemo(() => ..., [query]) with a direct calculation and compare the console.
Complete React Course  |  Part 4  |  Performance Optimization
M16Practice 4 of 10  •  Easy

Example 4: useCallback + memo Child

● Easy💼 Real project: Passing handlers to optimised children
AddButton is memoized, but the parent creates a NEW onAdd function every render, so memo is useless. useCallback keeps the same function, and the child finally skips renders.
App.jsxRenderCount.jsx
LIVELive Preview
Console
✍ Try it yourself Type in the input and compare the two badges.
Only the button without useCallback re-renders while typing.
Complete React Course  |  Part 4  |  Performance Optimization
M16Practice 5 of 10  •  Easy

Example 5: Lazy-Loaded Chart with Suspense

● Easy💼 Real project: Reports pages, admin dashboards
lazy() loads HeavyChart only when the user clicks "Show report". Suspense shows a fallback while it loads. We add a fake 1.2 s delay to simulate a slow network.
App.jsxHeavyChart.jsx
LIVELive Preview
Console
✍ Try it yourself Press Re-run, then click "Show report". The console log appears only after you click.
In a real Vite build, HeavyChart.jsx becomes its own .js file downloaded on demand.
Complete React Course  |  Part 4  |  Performance Optimization
M16Practice 6 of 10  •  Medium

Example 6: 2,000-Row Search with useDeferredValue

●● Medium💼 Real project: Big tables and search results
The list is slow on purpose (each row does extra work). With useDeferredValue, the input stays smooth: React shows your typing first and updates the list when it has time. The faded list means "updating".
App.jsx
LIVELive Preview
Console
✍ Try it yourself Type fast. Then replace query={deferred} with query={query} and type again: notice the input lag.
Without deferring, every key press waits for the slow list to render.
Complete React Course  |  Part 4  |  Performance Optimization
M16Practice 7 of 10  •  Medium

Example 7: useTransition for a Slow Tab

●● Medium💼 Real project: Dashboards with heavy tabs
The "Posts" tab renders slowly. Wrapping the tab change in startTransition keeps buttons responsive: you can click another tab while Posts is still rendering, and isPending shows a hint.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Click "posts" then immediately "contact": the app switches to contact without waiting. Try again without startTransition (call setTab directly).
Without a transition the UI freezes until Posts finishes rendering.
Complete React Course  |  Part 4  |  Performance Optimization
M16Practice 8 of 10  •  Medium

Example 8: Prime Calculator: Measure with and without useMemo

●● Medium💼 Real project: Heavy maths in apps (reports, games)
Counting primes up to N is slow. We time it with performance.now(). With useMemo, clicking the unrelated counter costs ~0 ms; without it, every click recalculates.
App.jsx
LIVELive Preview
Console
✍ Try it yourself With useMemo ON, click "Unrelated click" and read the time. Turn it OFF and click again.
Only changing N should trigger the expensive calculation.
Complete React Course  |  Part 4  |  Performance Optimization
M16Practice 9 of 10  •  Hard

Example 9: Virtualized 10,000-Row List

●●● Hard💼 Real project: Huge tables: logs, chats, stock data
Rendering 10,000 rows creates 10,000 DOM nodes. Virtualization renders only the ~15 rows you can SEE, positioned inside a tall scroll area. Libraries: react-window, TanStack Virtual. Here is how they work inside.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Scroll fast to the bottom: still smooth! Open DevTools Elements in the Lab version and count the row divs.
Only about 15-20 row elements exist at any moment.
Complete React Course  |  Part 4  |  Performance Optimization
M16Practice 10 of 10  •  Hard

Example 10: Optimise a Dashboard (Render Chart)

●●● Hard💼 Real project: Real-world performance tuning
A dashboard with a clock ticking every second. Toggle Optimised to switch between plain components and memo + useMemo + useCallback versions. The bar chart shows total renders of each widget: watch the difference!
App.jsx
LIVELive Preview
Console
✍ Try it yourself Watch for 10 seconds unoptimised, then tick "Optimised": Stats and List stop growing while the clock keeps ticking.
Stable props (useMemo/useCallback) + memo children = they skip every clock tick.
Complete React Course  |  Part 4  |  Performance Optimization
M16Check your understanding

Quick Quiz

Click an answer. Wrong answers shake, the right one turns green and explains why.

Q1. What does React.memo(Comp) do?
✔ It shallow-compares props and reuses the last render if they are equal.
Q2. Why might a memo child still re-render every time?
✔ New references fail the === check. Stabilise them with useMemo/useCallback.
Q3. What is the FIRST step in performance work?
✔ Optimise what you measured; premature optimisation adds complexity.
🎯
Your score: 0 / 3 (first try counts)
Answer all 3 questions. Discuss any wrong answer with your partner before moving on.
Complete React Course  |  Part 4  |  Performance Optimization
M16Module summary

Recap: What you learned

  • ✔State change → component + all children re-render (usually fine).
  • ✔Measure with React DevTools Profiler before optimising.
  • ✔memo skips renders when props are equal; useMemo caches values; useCallback caches functions.
  • ✔lazy() + <Suspense> split code so pages load faster.
  • ✔useDeferredValue / useTransition keep typing and clicks smooth.
  • ✔Virtualize huge lists: render only visible rows.
🏆

Module 16 complete!

Finish all 10 examples and the "Try it yourself" tasks before moving on.

Practice folder: D:\react-practice\m16

➡ Next up

Part 5, Module 17: Advanced Patterns. Portals, error boundaries, compound components and React 19.
Complete React Course  |  Part 4  |  Performance Optimization

</> React Code Lab

Console