Migrating Zustand from version 3 to 4
• 4 min read
Introduction
Zustand is an awesome state management library for React that excels in its simplicity. Its just one hook that can then be called from anywhere in the app to access your store.
This simplicity has made it the perfect choice for personal small-medium sized apps because it requires little boilerplate and it comes baked in with some awesome middleware!
As an aside middleware is:
such as:
- Persisting to local storage
- Immer to make state changes easier to handle
- Store version and migration handling
I used this library to handle persisted data in a personal app of mine. Essentially its a CRUD app to manage Pokemon you run into in a playthrough of a game.
The important part however is that I have an obsession with keeping my libraries up-to-date (specifically in these small personal apps) so I decided to go ahead and migrate Zustand from version 3 to 4.
As a small disclaimer this article assumes you have some base level experience with Zustand.
Migration Process
First and foremost you can check out the changelog and any specific changes you may need to do on the Zustand GitHub repo changelog.
First, let's go ahead and update the library:
TypeScriptyarn add zustand
In my case, it updated to 4.1.1
.
All the previous APIs are backward compatible so keep in mind that the changes I'm going to make apply to Typescript.
Immer is a library that allows you to work with immutable state data in a more convenient way. Before we had to explicitly set the type like this and the function to use it:
TypeScriptconst immer =
<T extends State>(config: StateCreator<T>): StateCreator<T> =>
(set, get, api) =>
config(
(partial, replace) => {
const nextState =
typeof partial === 'function'
? produce(partial as (state: T) => T)
: (partial as T);
return set(nextState, replace);
},
get,
api
);
However, we can now just import it directly from Zustand's middleware, so we can delete the above code.
TypeScriptimport { immer } from 'zustand/middleware/immer';
Next, the section where you're creating the store requires a small change:
TypeScriptconst useStore = create<AppState>(
to
TypeScriptconst useStore = create<AppState>()(
It is now treated as a curried function.
Last but not least, I used the migrate
option in the PersisOptions
that Zustand provides to persist all data to local storage. However, the state of this app is now considered unknown
by Typescript.
We can fix this by explicitly declaring the type with an assert function. For the sake of brevity I did something like this:
TypeScript migrate: (persistedState, version) => {
assertAppState(persistedState);
The assert function looks something like this:
TypeScriptfunction assertAppState(val: unknown): asserts val is AppState {
if (typeof val !== 'object') {
throw new TypeError('Invalid app state');
}
}
This means anytime I call my state object afterward in the migrate
function then it will have the correct typing rather than unknown
.
Wrapping it up
Zustand is an awesome library that is simple to use. I recommend using it in any project you may be working on. If you have any other questions or stories about your migration process leave them in the comments below!
Let's connect
If you liked this feel free to connect with me on LinkedIn or Twitter
Check out my free developer roadmap and weekly tech industry news in my newsletter.
If you see any typos or errors you can edit the article directly on GitHub
Hi, I'm Diego Ballesteros 👋.
Stay on top of the tech industry and grow as a developerwith a curated selection of articles and news alongside personal advice, observations, and insight. 🚀
No spam 🙅♂️. Unsubscribe whenever.
You may also like these articles:
- 2023-06-05T00:41:29.681Z
- 2022-10-25T11:29:26.663Z
- 2022-10-17T14:44:10.229Z
- 2022-10-10T14:02:10.349Z