Skip to content

Instantly share code, notes, and snippets.

@leosh1d
Created December 23, 2022 22:17
Show Gist options
  • Save leosh1d/abccc1ab352126c667a49c2e0c9d9a6c to your computer and use it in GitHub Desktop.
Save leosh1d/abccc1ab352126c667a49c2e0c9d9a6c to your computer and use it in GitHub Desktop.
Zustand (^4.1.5) persist middleware with deferred hydration.
import { persist, PersistOptions } from "zustand/middleware"
import { StateCreator, StoreApi, StoreMutatorIdentifier } from "zustand/vanilla"
type DeferredPersistOptions<S extends object> = PersistOptions<S> & {
hydrateOnResolve?: Promise<void>
}
const DEFAULT_GET_STORAGE = () => localStorage
export default function deferredPersist<
S extends object,
CustomSetState extends [StoreMutatorIdentifier, unknown][] = [
StoreMutatorIdentifier,
unknown
][],
CustomGetState extends [StoreMutatorIdentifier, unknown][] = [
StoreMutatorIdentifier,
unknown
][]
>(
config: StateCreator<
S,
[...CustomSetState, ["zustand/persist", unknown]],
CustomGetState,
S
>,
deferredOptions: DeferredPersistOptions<S>
) {
const hydrateOnResolve = deferredOptions.hydrateOnResolve || Promise.resolve()
const getStorage = deferredOptions.getStorage || DEFAULT_GET_STORAGE
const options: PersistOptions<S> = {
...deferredOptions,
getStorage: () => ({
setItem: async (...args) =>
hydrateOnResolve.then(() => getStorage().setItem(...args)),
getItem: async (...args) =>
hydrateOnResolve.then(() => getStorage().getItem(...args)),
removeItem: async (...args) =>
hydrateOnResolve.then(() => getStorage().removeItem?.(...args)),
}),
}
return persist<S, CustomSetState, CustomGetState>(config, options)
}
@leosh1d
Copy link
Author

leosh1d commented Dec 23, 2022

Based on kinoadr gist

@tomtobac
Copy link

hey @leosh1d, thanks for sharing!
qq: how are you passing the hydration event as a promise to hydrateOnResolve?

@leosh1d
Copy link
Author

leosh1d commented Jan 12, 2023

@tomtobac

in store.ts create promise

let resolveHydratePromise

const HydratePromise = new Promise<void>(function (resolve) {
  resolveHydratePromise = resolve
})

pass HydratePromise to store

{
  hydrateOnResolve: HydratePromise,
  name: "yourStorageName",
}

export promise

  export const ResolveIsHydrated = resolveHydratePromise || function () {}

In _app.tsx resolve promise with useEffect

  useEffect(() => {
    ResolveIsHydrated()
  }, [])

@tomtobac
Copy link

hey @leosh1d, thank you so much! It works perfectly 🙇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment