@tanstack/svelte-store
Version:
Framework agnostic type-safe store w/ reactive framework adapters
101 lines (87 loc) • 2.47 kB
text/typescript
import type { Derived, Store } from '@tanstack/store'
export * from '@tanstack/store'
/**
* @private
*/
export type NoInfer<T> = [T][T extends any ? 0 : never]
type EqualityFn<T> = (objA: T, objB: T) => boolean
interface UseStoreOptions<T> {
equal?: EqualityFn<T>
}
export function useStore<TState, TSelected = NoInfer<TState>>(
store: Store<TState, any>,
selector?: (state: NoInfer<TState>) => TSelected,
options?: UseStoreOptions<TSelected>,
): { readonly current: TSelected }
export function useStore<TState, TSelected = NoInfer<TState>>(
store: Derived<TState, any>,
selector?: (state: NoInfer<TState>) => TSelected,
options?: UseStoreOptions<TSelected>,
): { readonly current: TSelected }
export function useStore<TState, TSelected = NoInfer<TState>>(
store: Store<TState, any> | Derived<TState, any>,
selector: (state: NoInfer<TState>) => TSelected = (d) => d as any,
options: UseStoreOptions<TSelected> = {},
): { readonly current: TSelected } {
const equal = options.equal ?? shallow
let slice = $state(selector(store.state))
$effect(() => {
const unsub = store.subscribe(() => {
const data = selector(store.state)
if (equal(slice, data)) {
return
}
slice = data
})
return unsub
})
return {
get current() {
return slice
},
}
}
export function shallow<T>(objA: T, objB: T) {
if (Object.is(objA, objB)) {
return true
}
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false
}
if (objA instanceof Map && objB instanceof Map) {
if (objA.size !== objB.size) return false
for (const [k, v] of objA) {
if (!objB.has(k) || !Object.is(v, objB.get(k))) return false
}
return true
}
if (objA instanceof Set && objB instanceof Set) {
if (objA.size !== objB.size) return false
for (const v of objA) {
if (!objB.has(v)) return false
}
return true
}
if (objA instanceof Date && objB instanceof Date) {
if (objA.getTime() !== objB.getTime()) return false
return true
}
const keysA = Object.keys(objA)
if (keysA.length !== Object.keys(objB).length) {
return false
}
for (let i = 0; i < keysA.length; i++) {
if (
!Object.prototype.hasOwnProperty.call(objB, keysA[i] as string) ||
!Object.is(objA[keysA[i] as keyof T], objB[keysA[i] as keyof T])
) {
return false
}
}
return true
}