Sitemap
Level Up Coding

Coding tutorials and news. The developer homepage gitconnected.com && skilled.dev && levelup.dev

ADVANCED ZUSTAND “1”. Zustand Mental Model: How it Differs from Redux?

3 min readFeb 20, 2026

--

Press enter or click to view image in full size

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.

Get Simuratli’s stories in your inbox

Join Medium for free to get updates from this writer.

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.

--

--

Simuratli
Simuratli

Written by Simuratli

MSc. High Energy and Plasma Physics | B.A. Computer Engineering | Content Creator. https://bento.me/simuratli