The previous module ended with an uncomfortable diagnosis: CicloUrbano's state is scattered across the whole application — the session in UserContext, the theme in ThemeContext, notices in NoticesContext, bookings in a reducer inside BookingsContext, the catalogue filter in the URL, and the search term in a useState inside CataloguePage — and nobody could say for sure where the next piece of data that shows up should go. The temptation at this point is to pick a library and trust it to sort out the mess. That's exactly the wrong decision, and in the wrong order. State gets classified first, the tool gets chosen second, because half the problems people try to solve with Redux disappear simply by putting each piece of data where it belongs. In this lesson you'll build a taxonomy of state, apply it to CicloUrbano's actual inventory, learn a decision tree that works for any new piece of data, understand the two symmetric mistakes — over-lifting and globalizing too soon — recover the distinction between state and derived value, and map out the options available. By the end you'll have a written audit of CicloUrbano and the plan for what each lesson in the module reorders.

Contents

  1. What "managing state" actually means
  2. The taxonomy: six types of state
  3. A type-by-type walk through CicloUrbano's inventory
  4. The decision tree
  5. The two symmetric mistakes: over-lifting and premature globalization
  6. Single source of truth and derived state
  7. The landscape of options: what exists and when you'd pick it
  8. Auditing CicloUrbano's current state
  9. The module's plan

  1. What "managing state" actually means

So far you've learned mechanisms: useState holds a value between renders, useReducer concentrates transitions, useContext avoids passing props through ten levels, useSearchParams puts a value in the URL. Managing state isn't learning one more mechanism. It's answering, for every piece of data in the application, three questions:

  1. Who owns it? That is, the single place where that data genuinely exists. Everything else should read it from there, not copy it.
  2. Who needs it? The set of components that read it or change it. That set determines how far up it has to go.
  3. When does it expire? A local value lives as long as the component does. Server data can go stale a second after it arrives. They're not the same kind of thing.

When these three questions get answered wrong, the familiar symptoms show up: two parts of the screen showing different figures for the same thing, a form that empties itself on navigation, a counter that resets for no reason, a theme change that repaints the entire catalogue, a useEffect syncing one piece of state with another. None of those symptoms gets cured by installing a package.

State management is, above all, a problem of placement. Choosing a library is the last decision, not the first.

  1. The taxonomy: six types of state

This is the lesson's central table. It's worth reading slowly, because the rest of the module leans on it.

Type What it is Example in CicloUrbano Where it should live Tool If you put it in the wrong place
Local UI state A visual detail that only affects the component that renders it A dropdown being open, a panel's active tab, whether UserMenu is expanded Inside the component itself useState, useToggle Lifted: the parent re-renders half the screen just to open a menu
Shared among a few Data that two or three sibling components coordinate around The selected bike in the catalogue, highlighted by BikeList and shown by BookingPanel In the nearest common ancestor Lifting state up (04-01) + props Global: it ends up in a store where it doesn't belong, and nobody knows who's changing it
Application-wide global Data read by half the application and that changes rarely Session (user, isOperator), visual theme, notices Near the root, accessible without props Context, Redux Toolkit, Zustand Passed through props: six levels of prop drilling with props that exist only to pass through
Server state A local copy of data that lives in a remote database Bikes, stations, bookings, users In a cache with its own lifecycle TanStack Query, RTK Query, the router's loader In useState or in Redux: you end up hand-writing loading, error, cancellation, revalidation, and invalidation
URL state Data that must be shareable by link or survive a reload The catalogue filter (?tipo=electrica), the detail page's bicicletaId In the address itself useSearchParams, useParams (06-02) In useState: the link you send a colleague won't show what you're seeing
Form state The values the user is typing, plus their validation The draft in BookingForm Next to the form, or in its own reducer Controlled useState, useReducer, React Hook Form Global: every keystroke dispatches an action and re-renders everything that reads the store

Notice a detail that turns out to be decisive in 07-06: server state isn't a variant of global state, it's a separate category. It shares with it the fact that half the application reads it, but it differs on the essentials: you don't own it, it goes stale on its own, and someone else can change it while you're looking at it.

  1. A type-by-type walk through CicloUrbano's inventory

The table is abstract until it's applied. Let's go data point by data point through what's already written in the project.

3.1 Local UI state: UserMenu's dropdown

// src/components/UserMenu.jsx
function UserMenu() {
  const [open, toggle] = useToggle(false);   // hook from 05-06
  const { user, signOut } = useUser();
  // …
}

open is the canonical example of local state: nobody outside UserMenu needs to know about it, it doesn't need to survive a navigation, and there's no sense in sharing it by link. If you lifted it to Layout, opening the menu would trigger a render of Layout and, with it, of Header, Breadcrumbs, and the entire <Outlet />. An enormous cost for nothing in return.

Rule: if writing the state inside the component doesn't bother anyone, leave it there. State moves up when someone needs it, not just in case.

3.2 Shared among a few: the selected bike

In the catalogue, BikeList visually marks the chosen card and BookingPanel shows its data. They're siblings, so the data lives in their common ancestor, CataloguePage, and flows down through props. It's exactly what you did in 04-01.

// src/pages/CataloguePage.jsx (excerpt)
const [selectedId, setSelectedId] = useState(null);
// …
<BikeList bikes={visible} selectedId={selectedId} onSelect={setSelectedId} />
<BookingPanel bike={visible.find((bike) => bike.id === selectedId)} />

Two levels of props aren't a problem. Prop drilling starts to hurt once you're three or four levels deep in components that receive a prop only to pass it along, without using it.

3.3 Application-wide global: session, theme, and notices

All three fit the same profile: lots of people read them, they're far apart in the tree, and they change rarely.

Data Who reads it How often it changes
user, isOperator Header, UserMenu, ProtectedRoute, RequireRole, WorkshopPage, BookingForm Very low: on sign-in and sign-out
Visual theme ThemeButton and the document's data-theme attribute Very low: whenever the user clicks it
Notices NoticeList renders them; anyone can trigger one Medium: on every operation that reports something

The fact that the change frequency is low is what makes context a reasonable choice for the first two. With notices, nuances already start to appear, and that nuance is the subject of 07-02.

3.4 Server state: bikes, stations, bookings, and users

Today, in CicloUrbano, they live in src/data/domain.js as imported constants. That's a teaching simplification that has held up for six modules, but it's a lie: in a real application, bici-002 is rented because someone rented it three minutes ago from another device, and your local copy has no idea.

// src/data/domain.js — the shortcut we've used up to now
export const bikes = [
  { id: 'bici-001', model: 'Classic Urban', type: 'urbana', status: 'disponible', stationId: 'est-01', pricePerHour: 2.5 },
  { id: 'bici-002', model: 'Electric Pro', type: 'electrica', status: 'alquilada', stationId: 'est-01', pricePerHour: 4.0 },
  // …
];

In 07-06 this becomes a fictitious API served with json-server, and with it comes the whole list of problems that justifies a dedicated library. For now it's enough to flag it in the inventory: these four collections are not client state.

3.5 URL state: the catalogue filter

You already solved this in 06-02: ?tipo=electrica lives in the address, gets read with useSearchParams, and chosenType doesn't exist as a useState. The proof that the decision was right is that you can copy the address bar, paste it to a colleague, and both of you see the same thing. That's the criterion: if the data needs to travel in a link, it goes in the URL, and nowhere else.

An important warning for the rest of the module: once Redux arrives, there'll be a very clear temptation to move the filter into the store "to have it all together." Don't. It would duplicate the source of truth, and you'd have to sync the URL and the store with an effect — exactly what useSearchParams avoids.

3.6 Form state: the booking draft

The draft in BookingForm is ephemeral, changes on every keystroke, and only matters while the form is open. It lives in bookingsReducer because it shares transitions with submission (submit_started, booking_created, submit_failed), not because it's global. It's a textbook case of why classification matters: the draft lives in a context, but it isn't global state.

  1. The decision tree

Whenever a new piece of data shows up, walk this from top to bottom and stop at the first "yes."

flowchart TD
    A["New piece of data"] --> B{"Does it come from a server?"}
    B -- Yes --> C["Server-state cache<br/>TanStack Query · 07-06"]
    B -- No --> D{"Must it be shareable<br/>by link or survive<br/>a reload?"}
    D -- Yes --> E["URL<br/>useSearchParams / useParams"]
    D -- No --> F{"Does only one<br/>component use it?"}
    F -- Yes --> G["Local useState"]
    F -- No --> H{"A few nearby<br/>siblings?"}
    H -- Yes --> I["Lift to the common ancestor<br/>+ props"]
    H -- No --> J{"Does half the app read it<br/>and does it change rarely?"}
    J -- Yes --> K["Context · 07-02"]
    J -- No --> L{"Does it change often,<br/>are there many consumers,<br/>or is the logic complex?"}
    L -- Yes --> M["Dedicated store<br/>Redux Toolkit · 07-03…07-05"]
    L -- No --> K

Two observations about the tree:

  • The first question is the server one, and that's deliberate. It's the one most people skip, and the one that saves the most work when you get it right. Putting remote data into a client store is the most expensive architecture mistake in this module.
  • The Redux branch comes last. Not because Redux is bad, but because reaching it means you've already ruled out five cheaper alternatives. When you reach Redux having ruled out those five, Redux is an excellent decision.

  1. The two symmetric mistakes: over-lifting and premature globalization

Beginners put state too low and suffer for it; intermediates overcorrect and put it too high. Both extremes have recognizable symptoms.

5.1 Over-lifting

It consists of lifting a piece of state higher than it needs to be, "in case someone else needs it someday."

Concrete symptoms:

  • Opening a dropdown re-renders the header, the breadcrumbs, and the entire page.
  • Components that receive five props and use one: the other four just pass through.
  • A parent with nine useState calls that have nothing to do with each other.
  • Changing a leaf component forces you to touch three files above it.
// ANTI-PATTERN: the state of a visual detail, lifted with no need
function Layout() {
  const [menuOpen, setMenuOpen] = useState(false);   // ← only UserMenu uses this
  return (
    <div>
      <Header menuOpen={menuOpen} onToggleMenu={setMenuOpen} />
      <Outlet />
    </div>
  );
}

Header doesn't use menuOpen: it only carries it down to UserMenu. And every click on the menu triggers a render of Layout — that is, of the entire application.

5.2 Premature globalization

It consists of installing a global-state library before you have a problem that justifies it.

Concrete symptoms:

  • A store with menuOpen, modalVisible, and activeTab: local state disguised as global.
  • Actions dispatched by only one component and read by that same component.
  • Five files touched (action, reducer, selector, component, test) to add a checkbox.
  • Nobody can remove a field from the store because nobody knows who reads it.
Over-lifting Premature globalization
What goes wrong State climbs higher than necessary Everything ends up in a single store
Main cost Unnecessary renders and pass-through props Complexity, indirection, and ceremony
How to spot it Unused props, cascading renders Local state inside the store
How to fix it Push state back down to the component that uses it Pull out of the store whatever isn't global

The fix for both is the same sentence: state should live at the lowest point that's still common to all of its consumers. No higher, no lower.

  1. Single source of truth and derived state

Here we pick back up, with bigger consequences, something you already saw in 05-01: don't store as state anything you can compute from other state.

// ANTI-PATTERN: duplicated state synced with an effect
const [bikes, setBikes] = useState(initialData);
const [type, setType] = useState('todos');
const [visible, setVisible] = useState(initialData);   // ← a derived value stored as state

useEffect(() => {
  setVisible(bikes.filter((bike) => type === 'todos' || bike.type === type));
}, [bikes, type]);

Three chained problems: there's one extra render (React paints with a stale visible and repaints after the effect runs), there's a window where visible and type contradict each other, and anyone who changes bikes in the future without going through the effect breaks the consistency.

// CORRECT: the derived value is computed during render
const [bikes, setBikes] = useState(initialData);
const type = params.get('tipo') ?? 'todos';           // the URL is the source of truth

const visible = bikes.filter((bike) => type === 'todos' || bike.type === type);

visible stops existing as state. It can't drift out of sync, because it's recomputed on every render from the only two sources of truth. If that calculation ever turns out to be expensive, the answer is to memoize it with useMemo (08-03), not turn it into state.

Cases of derived state that show up in CicloUrbano and shouldn't be stored:

Value Computed from Never store it because
isOperator user.role === 'operario' It drifts out of sync when the session changes
visible bikes + the URL filter + the search term It drifts out of sync on every filter change
canSubmit Validation errors + submitState It drifts out of sync while typing in the form
bookingTotal pricePerHour × hours It drifts out of sync when the hours change
freeDocks station.docks − bikes at the station It drifts out of sync when a bike moves

Rule: if you can write the value as an expression of other values, it's derived. Write it as an expression.

  1. The landscape of options: what exists and when you'd pick it

This table is the map of the terrain. It isn't a single recommendation: the column that matters is "when you'd pick it."

Option What it solves When you'd pick it Entry cost
Props + lifting state Coordinating a few nearby components Whenever the distance is one to three levels None: it's already in React
Context Avoiding pass-through props for ambient data Session, theme, locale, notices: few changes, many readers Low, but with a performance cost if the value changes often (07-02)
useReducer + context Complex, shared transition logic A domain with many actions and a wide tree of consumers Low: no new dependencies
Redux Toolkit A single store, traceable actions, debugging tools Large applications, multi-person teams, state logic that needs auditing Medium: one dependency, a vocabulary, and a folder structure
Zustand A minimal global store with granular selection, no provider You want global state without Redux's ceremony and don't need time travel Very low: one file and one hook
Jotai Atomic state: small units that combine, re-rendering only whoever reads them Many independent pieces of state with dependencies between them Low, but it requires thinking in atoms, not objects
TanStack Query Server state: cache, loading, error, revalidation, invalidation As soon as the application reads data from an API. Almost always Medium, and it pays for itself on the first screen

On Zustand and Jotai, just enough to place them, with no tutorial, because this module teaches Redux Toolkit:

  • Zustand defines a store as a function that returns state and actions, and components subscribe with a selector: const user = useStore((s) => s.user). There's no provider, no actions with a type, and only whoever reads what changed gets re-rendered. It's the usual alternative when Redux feels like too much ceremony.
  • Jotai flips the model: instead of one big object, it defines small atoms (const themeAtom = atom('light')) that get read with useAtom. Derived atoms recompute themselves, and only whoever depends on the atom that changed gets re-rendered.

Both are legitimate options, and neither replaces TanStack Query for remote data. The most common choice today in a mid-sized application isn't "Redux or context," but "one tool for client state and another for server state."

  1. Auditing CicloUrbano's current state

With the taxonomy in hand, here's a snapshot of the application at the end of module 6, and the verdict on each piece.

Data Where it lives today Actual type Verdict Lesson that handles it
user, isOperator, loadingSession UserContext Global Correct; the value needs to be stabilised 07-02, later sessionSlice in 07-04
Visual theme ThemeContext Global Correct, and it stays that way for the whole module 07-02
notices, showNotice NoticesContext Global Correct, but state needs to be separated from actions 07-02
bookings, draft, submitState, error BookingsContext with useReducer Mixed: the list is server state, the draft is form state To be split 07-04 and 07-06
Filter ?tipo= URL URL state Correct; not moved to Redux Stays as is
Search term useState in CataloguePage Local today, shared as soon as another screen reads it Worth revisiting 07-04 (catalogueSlice)
bikes, stations Constants in src/data/domain.js Server state To be moved to a query cache 07-06
selectedId useState in CataloguePage Shared among a few Correct: stays where it is Stays as is
open in UserMenu, modals Local useState / useToggle Local UI state Correct: never enters the store Stays as is

Two conclusions from the audit worth underlining before moving on:

  1. Most of what's there is already in the right place. This module isn't going to throw away the work from modules 5 and 6: it's going to reorder three things and extract a fourth.
  2. The most serious problem isn't context — it's that domain data is being treated as client state. That's what 07-06 fixes, and it's the highest-impact change in the whole module.

  1. The module's plan

flowchart LR
    A["07-01<br/>Classify"] --> B["07-02<br/>Context done right"]
    B --> C["07-03<br/>Redux store"]
    C --> D["07-04<br/>Slices and selectors"]
    D --> E["07-05<br/>Connecting to React"]
    E --> F["07-06<br/>Server state"]
Lesson What it reorders in CicloUrbano
07-02 Context API Splits BookingsContext into state and actions, composes the four providers into a single Providers, and establishes where context stops paying off
07-03 Redux Introduction Creates src/store/store.js with configureStore and provides it in main.jsx, with DevTools working
07-04 Actions and Reducers Writes bookingsSlice, catalogueSlice, and sessionSlice with their selectors and their async loading
07-05 Connecting to React Rewrites CataloguePage, BookingsPage, BookingsPanel, and UserMenu with useSelector and useDispatch, and decides what stays out
07-06 Server State Spins up the API with json-server, replaces useFetchBikes with useBikes built on TanStack Query, and settles the final architecture

Common Mistakes and Tips

Mistake 1: choosing the library before classifying the state. "Let's use Redux" isn't an architecture decision, it's a postponement. Classify first; you'll often discover that half of your "global state" was server state and the other half was local state.

Mistake 2: treating server data as ordinary state. It's the most expensive and most frequent mistake. If the data has an id that exists in a database, it isn't yours: it's a copy that expires.

Mistake 3: storing derived values. Every useEffect whose only job is to setSomething(...) from other state is a badly stored derived value. Delete it and compute the expression during render.

Mistake 4: moving to a global store what already lives in the URL. You end up with two sources of truth and an effect syncing them. The URL always wins for whatever needs to be shareable by link.

Mistake 5: confusing "many components use it" with "it changes a lot." These are different axes that determine different things: the first decides where it lives, the second decides which tool to use. Data read by twenty components that changes once per session is context's ideal case; data read by twenty components that changes on every keystroke is the worst case.

Tip 1: write the inventory down. A three-column table — data, type, where it lives — in the project's README avoids months of arguments. When someone adds a new piece of data, the table tells them where to put it.

Tip 2: always start with local useState. Lifting a piece of state later is a ten-minute refactor. Pulling it back down from a global store that eight components have already hooked into is an afternoon-long refactor.

Tip 3: measure before optimising placement. If you suspect a context is causing extra renders, check it with the Profiler (08-05) instead of restructuring blindly.

Exercises

Exercise 1. Classify these six new pieces of CicloUrbano data according to section 2's taxonomy, and state where each should live and with what tool:

  1. The text the operator types into WorkshopPage's search box.
  2. The list of incidents for a station, coming from GET /estaciones/est-02/incidencias.
  3. The chosen price unit (€/hour or €/day), which affects every card and the detail page.
  4. Whether the BikeCard card shows its long description expanded.
  5. The catalogue's sort order (by price or by model), which needs to be shareable by link.
  6. The number of active bookings shown by the badge in Header.

Exercise 2. The following component has three state-management problems. Identify them and rewrite it.

function StationsPage() {
  const [stations, setStations] = useState([]);
  const [district, setDistrict] = useState('todos');
  const [visible, setVisible] = useState([]);
  const [totalDocks, setTotalDocks] = useState(0);
  const [openDetail, setOpenDetail] = useState(null);

  useEffect(() => {
    setVisible(stations.filter((st) => district === 'todos' || st.district === district));
  }, [stations, district]);

  useEffect(() => {
    setTotalDocks(visible.reduce((sum, st) => sum + st.docks, 0));
  }, [visible]);

  // …
}

Exercise 3. A colleague proposes: "Let's put the whole application in Redux: the bikes, the catalogue filter, the theme, UserMenu's dropdown, and the booking form's draft. That way it's all in one place and we always know where to look." Write a reasoned response, data point by data point, saying which ones should and which shouldn't, and why.

Solutions

Solution 1.

Data Type Where it lives Tool Reasoning
1. Workshop search box Local UI state (or form state) In WorkshopPage useState + useDebounce Only that screen uses it, and there's no point sharing it by link. If the team decides it should be linkable after all, it moves to the URL
2. A station's incidents Server state Query cache useQuery(['estaciones', estacionId, 'incidencias']) It comes from an API: the tree's first question gets a "yes," and that's where the walk ends
3. Price unit Application-wide global Near the root Context (or catalogueSlice if you're already using Redux) Many readers, very infrequent changes: an identical profile to the theme's
4. Expanded description Local UI state Inside BikeCard useToggle A per-card visual detail. Lifting it would force you to keep a map of open identifiers for nothing
5. Catalogue sort order URL state In the address useSearchParams, alongside ?tipo= The prompt says so: it needs to be shareable by link
6. Number of active bookings Derived from server state Not stored bookings.filter((b) => b.status === 'activa').length It isn't state: it's a count over the booking list. Storing it guarantees that one day it'll show a figure that doesn't match the list

Case 6 is the most instructive one: the right answer isn't "where do I store it" but "don't store it."

Solution 2. The three problems:

  1. visible is a derived value stored as state, synced with an effect. Both the state and the effect are unnecessary.
  2. totalDocks is a derived value of a derived value, with a second, chained effect. Every change to district triggers three renders: one with stale data, one after the first effect, and one after the second.
  3. stations is server state stored in useState, with no loading, no error, and no cancellation. It's 07-06's candidate.
function StationsPage() {
  // 3) Server state: in 07-06 this becomes useQuery
  const { data: stations = [], isPending, isError } = useStations();

  // Real state: only two pieces
  const [district, setDistrict] = useState('todos');
  const [openDetail, setOpenDetail] = useState(null);

  // 1) and 2) Derived values: expressions, not state
  const visible = stations.filter((st) => district === 'todos' || st.district === district);
  const totalDocks = visible.reduce((sum, st) => sum + st.docks, 0);

  if (isPending) return <LoadingIndicator message="Loading stations…" />;
  if (isError) return <Notice tone="error" text="Couldn't load the stations." />;
  // …
}

Five state variables become two. Both useEffects disappear, and with them the intermediate renders and the windows of inconsistency. openDetail stays: it's legitimate local UI state.

Solution 3. A reasoned response, data point by data point:

  • Bikes: no. They're server state. In Redux you'd have to hand-write loading, error handling, cancellation, revalidation on returning to the tab, and invalidation after every write. They belong in a query cache (07-06). If the team would rather not add another dependency, the reasonable alternative is RTK Query, which is the same idea inside Redux, not a hand-written slice.
  • Catalogue filter: no. It already lives in the URL, which is the correct source of truth because it needs to be shareable by link and survive a reload. Moving it into the store would create a second copy and a syncing effect, with guaranteed inconsistency.
  • Theme: not needed. It changes once per session and gets read by a ThemeButton plus the document's data-theme attribute. Context solves it with no dependencies. Putting it in Redux isn't wrong, but it adds nothing: there's no logic to audit and no actions to trace.
  • UserMenu's dropdown: absolutely not. It's local UI state. Putting it in the store would turn it global; every opening would dispatch an action that clutters the DevTools history, and it would force you to check who else reads that field before touching it.
  • The form's draft: no. It changes on every keystroke. In the store, every key would be an action and a comparison cycle for every subscriber. It stays next to the form, in useState or in its reducer.
  • What genuinely belongs in Redux: the session (sessionSlice), the persistent catalogue criteria that don't belong in the URL — like the search term, if it ends up shared across screens — and the client-side booking logic, which is the part with transitions worth auditing.

And the underlying argument against the proposal: "having it all in one place" isn't an advantage if that place stops telling you anything. A store with two hundred fields, a hundred and fifty of which are local, is just as hard to reason about as having no store at all; on top of that, it buries Redux's most valuable tool — the action history — under the noise of menus opening.

Conclusion

Before installing anything, you've brought order. You know that managing state means answering three questions for each piece of data — who owns it, who needs it, and when it expires — and that the answer sorts into six types: local UI state, shared among a few, application-wide global, server state, URL state, and form state, each with its natural home and its tool. You have a decision tree that starts with the question that saves the most — "does it come from a server?" — and that leaves the dedicated store as the last branch, the one you reach only after ruling out five cheaper alternatives. You know the two symmetric mistakes and their symptoms: over-lifting, which turns every click into a render of the whole application and fills components with pass-through props, and premature globalization, which puts menuOpen in a store and forces you to touch five files to add a checkbox. And you've reinforced 05-01's rule with more weight behind it: a single source of truth, with everything else computed, because visible, isOperator, canSubmit, bookingTotal, and freeDocks aren't state, and storing them only guarantees that one day they'll contradict each other.

On top of that framework you've placed the real options — props and lifting, context, useReducer + context, Redux Toolkit, Zustand, Jotai, and TanStack Query — with their entry cost and their right moment, and you've done the CicloUrbano audit: most of it is already in the right place, the filter will stay in the URL and the dropdowns will stay local, but BookingsContext mixes form state with data that's actually server state, and src/data/domain.js is pretending to be a database.

The first piece to reorder is the one already in your hands. Context isn't just a mechanism for avoiding props: it's a state-management strategy, with a complete per-domain module pattern, a very concrete performance cost that so far has only been mentioned in passing, and a clear limit beyond which another tool makes more sense. The next lesson is Context API.

React Course

Module 1: Getting Started with React

Module 2: React Components

Module 3: Working with Events

Module 4: Advanced Component Concepts

Module 5: React Hooks

Module 6: Routing in React

Module 7: State Management

Module 8: Performance Optimization

Module 9: Testing React Applications

Module 10: Advanced Topics

Module 11: Project: Building a Complete Application

© Copyright 2026. All rights reserved