Skip to main content

Shared State: The Problem a Store Solves

In week 12 we built a login with useState, stored the token in localStorage and protected routes with an AuthGuard. It works, but every screen that needs to know who the user is has to read localStorage on its own, and no component finds out when the session changes.

Before installing any library it is worth understanding the problem precisely. A state tool is chosen well only when you know which pain it removes. In this lesson we go from the simplest case (useState) to the point where a global store stops being a luxury.


1. What Is State​

State is the information that changes over time and on which what you see on screen depends. When the state changes, React draws the interface again.

Not all state is the same type, and mixing them up is the source of most tangles:

TypeExample in our appWhere it should live
Local to a componentText typed in the login input, whether a menu is openuseState inside the component
Shared between componentsAuthenticated user, notifications, cart, visual themeA store (what we will see this week)
From the server (server state)List of users returned by GET /usersA data layer (fetch, cache); it is not the same as UI state
From the URLSearch filters, current page, route idThe URL itself (searchParams, params)
Rule of thumb

Always start with the most local state possible. Lift it only when two components that are not parent and child need the same data.


2. How State Travels in React​

Walk through the component tree of a page. You will see where state lives, how it is lifted to the common ancestor (lifting state up), why prop drilling appears and what Context achieves (and what it does not).

The interactive graphics are in Spanish

The interactive pieces of this chapter are shared with the Spanish version and keep their Spanish text. The example uses the gym routines domain from Computación en Internet 3.

How state travels through the component tree

The essentials of that walkthrough:

  1. Data only goes down the tree (from parent to child, as props). There is no sideways path between branches.
  2. Lifting the state to the common ancestor solves the first problem, but forces you to pass props through components that do not use them (prop drilling).
  3. Context takes a direct jump, but when its value changes all consumers are rendered again, even if they only care about one part.

3. A Concrete Problem: The User Session​

Think about the login case study application from week 12. The session is needed by:

  • The AuthGuard, to decide whether to let the user in.
  • The navigation bar, to show the name and the "Log out" button.
  • The dashboard, to request GET /users with the token.
  • Any form that signs a request.

With what we know so far there are three ways out, all with a cost:

Three ways to share the session
// 1. Props: the data goes down through every intermediate (prop drilling)
<AppLayout user={user}>
<Navbar user={user} /> {/* Navbar only forwards it */}
</AppLayout>

// 2. Context: a Provider and useContext, but every consumer re-renders
<AuthContext.Provider value={{ user, token, login, logout }}>
<App />
</AuthContext.Provider>

// 3. localStorage directly (what we did in week 12): nobody finds out about the change
const token = session.getToken();

None is wrong. What happens is that as the app grows, each one accumulates debt: repeated props, re-renders you cannot see, or screens that do not update when you log out somewhere else in the app.


4. What Is a Global Store​

A store is an object that lives outside the component tree and keeps two things together:

  1. The shared state (user, token, notifications).
  2. The actions that modify it (login(), logout(), addNotification()).

Any component subscribes to the part it cares about and React draws it again only when that part changes.

Analogy: the faculty bulletin board. Anyone can go and read the notice they care about, and when the coordination office changes a notice, only the people who were looking at that notice find out. Nobody carries the paper by hand to every classroom.

What changes compared to the previous approaches:

BeforeWith a store
Data goes down through propsThe component reads it directly from the store
Changes go up through callbacksThe component calls the store action
Context re-renders everyoneEach component subscribes only to what it uses (selectors)
State lives in the treeState lives outside, and survives a component unmounting

When It Is NOT Needed​

A global store is not the answer to everything. Do not use it for:

  • The value of an input while the user types (it is local state).
  • Data from the server that is requested again on every screen (that is server state; a store can hold it, but it does not replace a cache strategy).
  • Something used only by one component and its direct children.

5. The Week at a Glance​

This week we walk the path in this order:

1

Concept: the store and its flow

What Zustand is, what a store looks like, what set is, what a selector is and how it compares with Redux and Context. See Zustand Concepts.

2

Setup in Next.js

Installation, typing with TypeScript, store location by feature, 'use client' and the care needed with the server. See Zustand Setup.

3

Capabilities

Selectors, useShallow, async actions, middleware (devtools, persist, immer) and use outside React. See Zustand Capabilities.

4

Practical case

We migrate the week 12 session to a store with persistence and build a notification system. See Practical Case.


Self-Assessment Quiz​

Cargando cuestionario...