UNPKG

@tanstack/store

Version:

Framework agnostic type-safe store w/ reactive framework adapters

1 lines 8.33 kB
{"version":3,"file":"derived.cjs","sources":["../../src/derived.ts"],"sourcesContent":["import { Store } from './store'\nimport { __derivedToStore, __storeToDerived } from './scheduler'\nimport type { Listener } from './types'\n\nexport type UnwrapDerivedOrStore<T> =\n T extends Derived<infer InnerD>\n ? InnerD\n : T extends Store<infer InnerS>\n ? InnerS\n : never\n\ntype UnwrapReadonlyDerivedOrStoreArray<\n TArr extends ReadonlyArray<Derived<any> | Store<any>>,\n> = TArr extends readonly [infer Head, ...infer Tail]\n ? Head extends Derived<any> | Store<any>\n ? Tail extends ReadonlyArray<Derived<any> | Store<any>>\n ? [UnwrapDerivedOrStore<Head>, ...UnwrapReadonlyDerivedOrStoreArray<Tail>]\n : []\n : []\n : []\n\n// Can't have currVal, as it's being evaluated from the current derived fn\nexport interface DerivedFnProps<\n TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>,\n TUnwrappedArr extends\n UnwrapReadonlyDerivedOrStoreArray<TArr> = UnwrapReadonlyDerivedOrStoreArray<TArr>,\n> {\n // `undefined` if it's the first run\n /**\n * `undefined` if it's the first run\n * @privateRemarks this also cannot be typed as TState, as it breaks the inferencing of the function's return type when an argument is used - even with `NoInfer` usage\n */\n prevVal: unknown | undefined\n prevDepVals: TUnwrappedArr | undefined\n currDepVals: TUnwrappedArr\n}\n\nexport interface DerivedOptions<\n TState,\n TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>,\n> {\n onSubscribe?: (\n listener: Listener<TState>,\n derived: Derived<TState>,\n ) => () => void\n onUpdate?: () => void\n deps: TArr\n /**\n * Values of the `deps` from before and after the current invocation of `fn`\n */\n fn: (props: DerivedFnProps<TArr>) => TState\n}\n\nexport class Derived<\n TState,\n const TArr extends ReadonlyArray<\n Derived<any> | Store<any>\n > = ReadonlyArray<any>,\n> {\n listeners = new Set<Listener<TState>>()\n state: TState\n prevState: TState | undefined\n options: DerivedOptions<TState, TArr>\n\n /**\n * Functions representing the subscriptions. Call a function to cleanup\n * @private\n */\n _subscriptions: Array<() => void> = []\n\n lastSeenDepValues: Array<unknown> = []\n getDepVals = () => {\n const prevDepVals = [] as Array<unknown>\n const currDepVals = [] as Array<unknown>\n for (const dep of this.options.deps) {\n prevDepVals.push(dep.prevState)\n currDepVals.push(dep.state)\n }\n this.lastSeenDepValues = currDepVals\n return {\n prevDepVals,\n currDepVals,\n prevVal: this.prevState ?? undefined,\n }\n }\n\n constructor(options: DerivedOptions<TState, TArr>) {\n this.options = options\n this.state = options.fn({\n prevDepVals: undefined,\n prevVal: undefined,\n currDepVals: this.getDepVals().currDepVals as never,\n })\n }\n\n registerOnGraph(\n deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps,\n ) {\n for (const dep of deps) {\n if (dep instanceof Derived) {\n // First register the intermediate derived value if it's not already registered\n dep.registerOnGraph()\n // Then register this derived with the dep's underlying stores\n this.registerOnGraph(dep.options.deps)\n } else if (dep instanceof Store) {\n // Register the derived as related derived to the store\n let relatedLinkedDerivedVals = __storeToDerived.get(dep)\n if (!relatedLinkedDerivedVals) {\n relatedLinkedDerivedVals = new Set()\n __storeToDerived.set(dep, relatedLinkedDerivedVals)\n }\n relatedLinkedDerivedVals.add(this as never)\n\n // Register the store as a related store to this derived\n let relatedStores = __derivedToStore.get(this as never)\n if (!relatedStores) {\n relatedStores = new Set()\n __derivedToStore.set(this as never, relatedStores)\n }\n relatedStores.add(dep)\n }\n }\n }\n\n unregisterFromGraph(\n deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps,\n ) {\n for (const dep of deps) {\n if (dep instanceof Derived) {\n this.unregisterFromGraph(dep.options.deps)\n } else if (dep instanceof Store) {\n const relatedLinkedDerivedVals = __storeToDerived.get(dep)\n if (relatedLinkedDerivedVals) {\n relatedLinkedDerivedVals.delete(this as never)\n }\n\n const relatedStores = __derivedToStore.get(this as never)\n if (relatedStores) {\n relatedStores.delete(dep)\n }\n }\n }\n }\n\n recompute = () => {\n this.prevState = this.state\n const { prevDepVals, currDepVals, prevVal } = this.getDepVals()\n this.state = this.options.fn({\n prevDepVals: prevDepVals as never,\n currDepVals: currDepVals as never,\n prevVal,\n })\n\n this.options.onUpdate?.()\n }\n\n checkIfRecalculationNeededDeeply = () => {\n for (const dep of this.options.deps) {\n if (dep instanceof Derived) {\n dep.checkIfRecalculationNeededDeeply()\n }\n }\n let shouldRecompute = false\n const lastSeenDepValues = this.lastSeenDepValues\n const { currDepVals } = this.getDepVals()\n for (let i = 0; i < currDepVals.length; i++) {\n if (currDepVals[i] !== lastSeenDepValues[i]) {\n shouldRecompute = true\n break\n }\n }\n\n if (shouldRecompute) {\n this.recompute()\n }\n }\n\n mount = () => {\n this.registerOnGraph()\n this.checkIfRecalculationNeededDeeply()\n\n return () => {\n this.unregisterFromGraph()\n for (const cleanup of this._subscriptions) {\n cleanup()\n }\n }\n }\n\n subscribe = (listener: Listener<TState>) => {\n this.listeners.add(listener)\n const unsub = this.options.onSubscribe?.(listener, this)\n return () => {\n this.listeners.delete(listener)\n unsub?.()\n }\n }\n}\n"],"names":["Store","__storeToDerived","__derivedToStore"],"mappings":"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"}