Skip to content

Commit

Permalink
feat!: switch to zustand official persistence middleware
Browse files Browse the repository at this point in the history
BREAKING CHANGE: The persistence feature will now be bound the the zustand persistence middleware API itself. Zfy's solution has been built when zustand did not provide that ability. Therefore, it has no reason to still exist now. This change also lead to the removal of `initZfy()` method as its not required anymore.
  • Loading branch information
CharlesMangwa committed Feb 1, 2022
1 parent 55e6a96 commit 3b29dd6
Show file tree
Hide file tree
Showing 14 changed files with 194 additions and 262 deletions.
Empty file removed index.d.ts
Empty file.
2 changes: 1 addition & 1 deletion src/core/PersistGate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { useEffect, memo } from 'react'
import type { CreateStoreType } from '../types'

import useRehydrate from './use-rehydrate'
import { validatePersistGate } from '../internals/validation'
import { validatePersistGate } from '../internals/validations'

interface PropsType<StoresType> {
children: JSX.Element | JSX.Element[] | (() => JSX.Element)
Expand Down
26 changes: 13 additions & 13 deletions src/core/create-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,43 @@ import type {
CreateStoreOptionsType,
} from '../types'

import { validateCreateStore } from '../internals/validation'
import { validateCreateStore } from '../internals/validations'
import createMiddlewares from '../internals/middlewares/create-middlewares'

/**
* Function that creates and returns a zustand store.
* @param name - `string`— Name of the store.
* @param storeName - `string`— Name of the store.
* @param data - `Record<string, any>`— Initial data of the store.
* @param options - `CreateStoreOptionsType`— Optional. Config to use for store setup.
*/
export default function <
StoresDataType extends Record<string, any>,
StoreNameType extends keyof StoresDataType = keyof StoresDataType
>(
name: StoreNameType,
storeName: StoreNameType,
data: StoresDataType[StoreNameType],
options?: CreateStoreOptionsType<StoresDataType, StoreNameType>
): CreateStoreType<StoresDataType[StoreNameType]> {
validateCreateStore<StoresDataType, StoreNameType>({ name, data, options })
validateCreateStore<StoresDataType, StoreNameType>({
storeName,
data,
options,
})

const applyMiddlewares = createMiddlewares(options) as (
const applyMiddlewares = createMiddlewares(storeName, options) as (
n: StoreNameType,
s: CreateStoreConfigType<StoresDataType[StoreNameType]>
) => CreateStoreConfigType<StoresDataType[StoreNameType]>

return create<StoreType<StoresDataType[StoreNameType]>>(
applyMiddlewares(name, (set) => ({
applyMiddlewares(storeName, (set) => ({
data,
update: (producer): void =>
set(
produce((currentStore: StoreType<StoresDataType[StoreNameType]>) => ({
...currentStore,
data: producer(currentStore.data),
}))
produce((currentStore: StoreType<StoresDataType[StoreNameType]>) => {
producer(currentStore.data)
})
),
rehydrate: (persistedData: {
data: StoresDataType[StoreNameType]
}): void => set(persistedData),
reset: (): void => set({ data }),
}))
)
Expand Down
1 change: 0 additions & 1 deletion src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export { default as initZfy } from './init-zfy'
export { default as PersistGate } from './PersistGate'
export { default as createStore } from './create-store'
export { default as useRehydrate } from './use-rehydrate'
13 changes: 0 additions & 13 deletions src/core/init-zfy.ts

This file was deleted.

31 changes: 8 additions & 23 deletions src/core/use-rehydrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ import { useState, useEffect } from 'react'

import type { CreateStoreType } from '../types'

import { getConfig } from '../internals/config'
import { validateUseRehydrate } from '../internals/validation'
import { validateUseRehydrate } from '../internals/validations'

/**
* Hooks that rehydrates persisted stores on app launch.
* @param stores - `Record<string, UseStore<StoreType<any>>>>`— Object containing zustand stores to rehydrate.
* @param stores - `Record<string, CreateStoreType<any>>`— Object containing zustand stores to rehydrate.
* @returns `boolean`— A boolean indicating whether rehydration process is done.
*/
export default function <
Expand All @@ -24,26 +23,12 @@ export default function <
;(async () => {
if (isRehydrated) return

const { deserialize, persistKey, storage } = getConfig()
for (let index = 0; index < storesNames.length; index += 1) {
const name = storesNames[index]
await Promise.resolve(
storage?.getItem?.(`@${persistKey}Store:${name}`)
).then(async (response) => {
if (response && typeof deserialize === 'function') {
await Promise.resolve(deserialize(response)).then(
(persistedData) => {
stores[name]?.getState()?.rehydrate?.(persistedData)
}
)
} else if (response) {
stores[name]?.getState()?.rehydrate?.(response)
}

if (index === storesNames.length - 1) {
setIsRehydrated(true)
}
})
for (const name of storesNames) {
await stores[name]?.persist?.rehydrate?.()

if (storesNames.indexOf(name) === storesNames.length - 1) {
setIsRehydrated(true)
}
}
})()
}, [isRehydrated, stores, storesNames])
Expand Down
34 changes: 0 additions & 34 deletions src/internals/config.ts

This file was deleted.

12 changes: 8 additions & 4 deletions src/internals/middlewares/create-middlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import type {
import logger from './logger-middleware'
import persist from './persist-middleware'

import { validateConfigForPersistence } from '../validation'
import { validateOptionsForPersistence } from '../validations'

const createMiddleware = <
StoresDataType extends Record<string, any>,
StoreNameType extends keyof StoresDataType = keyof StoresDataType
>(
storeName: StoreNameType,
options?: CreateStoreOptionsType<StoresDataType, StoreNameType>
) => {
const pipe =
Expand All @@ -21,16 +22,19 @@ const createMiddleware = <
n: StoreNameType,
s: CreateStoreConfigType<StoresDataType[StoreNameType]>
): CreateStoreConfigType<StoresDataType[StoreNameType]> =>
fns.length ? fns.reduce((c, f) => f(n, c), s) : s
fns.length ? fns.reduce((c, f) => f(n, c, options), s) : s

let middlewares: ZfyMiddlewareType<StoresDataType, StoreNameType>[] = []

if (options?.log) {
middlewares = [...middlewares, logger]
}
if (options && 'persist' in options) {
validateConfigForPersistence()
middlewares = [...middlewares, persist]
validateOptionsForPersistence(storeName, options)
middlewares = [
...middlewares,
persist as ZfyMiddlewareType<StoresDataType, StoreNameType>,
]
}
if (
options &&
Expand Down
32 changes: 21 additions & 11 deletions src/internals/middlewares/logger-middleware.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import type { CreateStoreConfigType, StoreType } from '../../types'
import { getConfig } from '../config'
import type {
StoreType,
CreateStoreConfigType,
CreateStoreOptionsType,
} from '../../types'

const logger =
const middleware =
<
StoresDataType extends Record<string, any>,
StoreNameType extends keyof StoresDataType
>(
store: StoreNameType,
config: CreateStoreConfigType<StoresDataType[StoreNameType]>
storeName: StoreNameType,
config: CreateStoreConfigType<StoresDataType[StoreNameType]>,
options?: CreateStoreOptionsType<StoresDataType, StoreNameType>
): CreateStoreConfigType<StoresDataType[StoreNameType]> =>
(set, get, api): StoreType<StoresDataType[StoreNameType]> =>
config(
Expand All @@ -17,20 +21,26 @@ const logger =

set(args)

if (getConfig().enableLogging) {
if (options?.log) {
const newState = get().data

console.group(
`%c🗂 ${store.toLocaleString().toLocaleUpperCase()} STORE UPDATED`,
`%c🗂 ${storeName
.toLocaleString()
.toLocaleUpperCase()} STORE UPDATED`,
'font-weight:bold'
)
console.log(
console.debug(
'%cprevState',
'font-weight:bold; color: #9E9E9E',
prevState
)
console.log('%cpayload', 'font-weight:bold; color: #27A3F7', payload)
console.log(
console.debug(
'%cpayload',
'font-weight:bold; color: #27A3F7',
(payload as StoreType<StoresDataType[StoreNameType]>).data
)
console.debug(
'%cnewState',
'font-weight:bold; color: #C6E40A',
newState
Expand All @@ -42,4 +52,4 @@ const logger =
api
)

export default logger
export default middleware
58 changes: 29 additions & 29 deletions src/internals/middlewares/persist-middleware.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import type { CreateStoreConfigType, StoreType } from '../../types'
import { getConfig } from '../config'
import type { StoreApi } from 'zustand'
import { persist, StoreApiWithPersist } from 'zustand/middleware'

const persist =
<
StoresDataType extends Record<string, any>,
StoreNameType extends keyof StoresDataType
>(
store: StoreNameType,
config: CreateStoreConfigType<StoresDataType[StoreNameType]>
): CreateStoreConfigType<StoresDataType[StoreNameType]> =>
(set, get, api): StoreType<StoresDataType[StoreNameType]> =>
config(
async (args) => {
set(args)
import type {
StoreType,
CreateStoreConfigType,
CreateStoreOptionsType,
} from '../../types'

const { persistKey, serialize, storage } = getConfig()
const middleware = <
StoresDataType extends Record<string, any>,
StoreNameType extends keyof StoresDataType
>(
storeName: StoreNameType,
config: CreateStoreConfigType<StoresDataType[StoreNameType]>,
options?: CreateStoreOptionsType<StoresDataType, StoreNameType>
): CreateStoreConfigType<
StoresDataType[StoreNameType],
StoreApi<StoreType<StoresDataType[StoreNameType]>> &
StoreApiWithPersist<StoreType<StoresDataType[StoreNameType]>>
> => {
const {
name = storeName as Exclude<StoreNameType, number | symbol>,
...rest
} = options?.persist ?? {}

if (typeof serialize === 'function') {
Promise.resolve(serialize({ data: get().data })).then((data) => {
storage?.setItem(`@${persistKey}Store:${store}`, data)
})
} else {
storage?.setItem(`@${persistKey}Store:${store}`, {
data: get().data,
})
}
},
get,
api
)
return persist((set, get, api) => config(set, get, api), {
name,
...(rest ? rest : {}),
})
}

export default persist
export default middleware
Loading

0 comments on commit 3b29dd6

Please sign in to comment.