
What is Global State?
- The data needed by multiple components
- Situations where we don’t want to perform prop drilling.
- Values that must stay synchronized across different parts of the UI
Example: Auth User
function Navbar() {
const user = ??? // needs access
}
function Dashboard() {
const user = ??? // also needs access
}If you lift the state too high, you get prop drilling:
<App user={user}>
<Layout user={user}>
<Navbar user={user} />
<Dashboard user={user} />
</Layout>
</App>Redux solves this problem using the Flux architecture.
But the real question is:
Does global state really need reducer + action + dispatch every time?
The Redux Mental Model:
Action -> Reducer -> Store -> Re-render
Example:
dispatch({ type: "SET_USER", payload: user }) //Action// Reducer
function reducer(state = initialState, action) {
switch (action.type) {
case "SET_USER":
return { ...state, user: action.payload }
default:
return state
}
}
//Component
const user = useSelector((state) => state.user)
Redux is predictable because it treats state changes like domain events.
This is powerful for:
- Complex business workflows
- Audit trails
- Large enterprise systems
- Time-travel debugging
But here’s the cost:
- Boilerplate
- Indirection
- Mental overhead
Even a simple UI state becomes ceremonial.
The Breaking Point: UI State vs Business State
Consider this example: You want to open a modal.
Redux version:
//Action
dispatch({ type: "OPEN_MODAL" })
//Reducer
case "OPEN_MODAL":
return { ...state, isModalOpen: true }
Now compare Zustand.
Zustand Mental Model (Subscription-Based State).
Zustand removes the event pipeline.
import { create } from "zustand"
const useStore = create((set) => ({
user: null,
isModalOpen: false,
setUser: (user) => set({ user }),
openModal: () => set({ isModalOpen: true }),
closeModal: () => set({ isModalOpen: false }),
}))Component:
const isModalOpen = useStore((s) => s.isModalOpen)
const openModal = useStore((s) => s.openModal)
Mental model:
- Components subscribe to specific slices
- Calling set() updates state
- Only affected subscribers re-render
The Critical Difference: Subscription Granularity
Redux subscription:
const user = useSelector((state) => state.user)
Zustand subscription:
const user = useStore((s) => s.user)
Superficially similar.
But here’s the important part:
In Zustand, each selector creates an independent subscription.
Example:
const user = useStore((s) => s.user)
const theme = useStore((s) => s.theme)
If theme changes:
→ user component does NOT re-render.
If you do this instead:
const state = useStore()
Now you subscribe to everything. That’s where performance problems begin. Understanding this is the first advanced step.
Performance Example: Why Mental Model Matters
Let’s simulate a heavy component:
function ExpensiveComponent() {
const user = useStore((s) => s.user)
console.log("rendering expensive component")
return <div>{user?.name}</div>
}Now imagine another part of the app updates:
set({ theme: "dark" })If your selector is correct:
→ ExpensiveComponent does NOT re-render.
If you destructured the entire state:
const { user, theme } = useStore()→ It will re-render unnecessarily.
This is why understanding the subscription model matters.
Architectural Philosophy Shift
Redux philosophy:
State change = domain event
A domain event represents a real-world event that occurred within the application’s business logic. "ORDER_CREATED" , "PAYMENT_COMPLATED"
Zustand philosophy:
State change = mutation through controlled setter
Redux enforces discipline through structure.
Zustand gives you flexibility — and expects you to be disciplined.
This is a big mental shift.
Redux is strict by design. Zustand is minimal by design.
Flux Pattern?
Redux is a direct Flux implementation. But Zustand not
Zustand:
- Does not impose Unidirectional flow
- Reducer is not necessary
- It leaves mutation control up to you.
But:
Not applying Zustand Flux doesn’t mean it’s chaotic.
Actually:
Zustand is a subscription-based state container.
This is a crucial difference in mental models.
When Redux Is Actually Better
Let’s be honest.
Redux is better when:
- You need strict event logs
- You require deterministic replay
- You have highly complex async flows
- You rely on domain-driven architecture
Example:
Order lifecycle in e-commerce:
ORDER_CREATED
ORDER_PAID
ORDER_SHIPPED
ORDER_REFUNDED
When Zustand Feels Right
- UI-heavy dashboards
- SaaS admin panels
- Chrome extensions
- Feature flags
- Modal management
- Filters & sorting
Most frontend apps are 70% UI state, 30% domain state.
Using Redux for everything can be overkill.
ADVANCED ZUSTAND "2". Selectors, Equality Functions, and Re-render Controls
ADVANCED ZUSTAND “1”. Zustand Mental Model: How it Differs from Redux? was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.