Using Zustand to manage application state in React
Published: 2023-07-26 17:59:20

---
title: Using Zustand to manage application state in React
published: true
date: 2023-01-25 00:00:00 UTC
tags: 
canonical_url: https://thomasrigby.com/articles/using-zustand-to-manage-application-state-in-react/
---

 ![A bike, for no reason](https://thomasrigby.com/images/zq6qy6LUu_-300.jpeg)

One of the downsides to Controller/Reducer-based state management is that a single piece of state needs to be passed from Component to Component up and down the cascade - _regardless of whether that component needs the state or not_.

This can cause an application to run slower than is desirable.

[Zustand](https://github.com/pmndrs/zustand) promises to alleviate this issue. And, to cut a long story short, it does.

Observe the _Hideous Spaceship_ of StateControllers!

```
ReactDOM.render(
  <React.StrictMode>
    <AppState>
      <UserState>
        <DownloadState>
          <DocumentsState>
            <ShareState>
              <ModalState>
                <ViewerState>
                  <InteractionsState>
                    <NotificationsState>
                      <App />
                    </NotificationsState>
                  </InteractionsState>
                </ViewerState>
              </ModalState>
            </ShareState>
          </DocumentsState>
        </DownloadState>
      </UserState>
    </AppState>
  </React.StrictMode>,
  document.getElementById("root")
);

```

This means that a piece of information from say `ModalState` has to be passed to `ViewerState` to `InteractionsState` to `NotificationsState` before the final destination of `<App/>` . This causes several unnecessary re-renders as well as being slow, costly, and frankly irritating.

**Zustand** uses a “Hook-based” approach that doesn’t pass state around components that do not directly need it.

This approach greatly reduces the “spaceship” and looks a little like this;

```
ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <BrowserRouter>
      <AppRoutes />
    </BrowserRouter>
  </React.StrictMode>
)

```

## Creating a Store

```
// Store/app.jsx

import create from 'zustand'

const useAppStore = create((set, get) => ({
    openSidebar: () => set(state => ({ sidebarIsOpen: true })),
  closeSidebar: () => set(state => ({ sidebarIsOpen: false })),
  sidebarIsOpen: false,
});

export default useAppStore;

```

Now we have a store we can `import` wherever we need to.

```
// Components/Nav.jsx

import useAppStore from '../../Store/app'

const Nav = (
) => {
  const openSidebar = useAppStore(state => state.openSidebar)
   return (
    <nav className="primary-nav">
      <button onClick={openSidebar}>🍔</button>
      <ul className="nav-list">...</ul>
    </nav>
  )
}

export default Nav


// Components/Sidebar.jsx

import useAppStore from '../../Store/app'

const Sidebar = ({ children }) => {
  const isSidebarOpen = useAppStore(state => state.isSidebarOpen)
  const closeSidebar = useAppStore(state => state.closeSidebar)

  return (
    <aside open={isSidebarOpen} className="sidebar">
      <button onClick={closeSidebar}>❎</button>
      {children}
    </aside>
  )
}

export default Sidebar

```

This way, the only Components that are affected by the change in `isSidebarOpen` are the `Nav` and the `Sidebar`. The main body of the app, for example, doesn’t have any knowledge of the state of the sidebar (because it doesn’t _need_ to know).

```
// Components/Main.jsx

const Main = ({ children }) => <main>{children}</main>

export default Main

```

However, if we need to, we _can_ make the `Main` aware of the state change as easily as `import`ing the Store…

```
// Components/Main.jsx

import useAppStore from '../../Store/app'

const Main = ({ children }) => {
  const sidebarIsOpen = useAppStore(state => state.sidebarIsOpen)
  return <main className={sidebarIsOpen && 'blur'}>{children}</main>
}

export default Main

```

## Amending state

Imagine, a while down the line, we get the request to track “sidebar opens” (for whatever mad reason - clients, eh?!). The only file we need to change is the Store.

```
// Store/app.jsx

import create from 'zustand'
import Analytics from 'analytics'

const useAppStore = create((set, get) => ({
    openSidebar: () => {
    Analytics.log({ event: 'sidebarOpen', timestamp: new Date() })
    set(state => ({ sidebarIsOpen: true }))
  },
  closeSidebar: () => set(state => ({ sidebarIsOpen: false })),
  sidebarIsOpen: false,
});

export default useAppStore;

```

## Conclusion

The learning curve is shallow enough - even for someone like me who isn’t the most React-savvy! It makes the state much more readable and replaceable too.