diff --git a/src/tanstack-query.ts b/src/tanstack-query.ts index 953fe4a23db3c6a3162e6a1b4f1b46478270de47..fd1f1a91a5845f211a9e8f2d62fa8e3351460fda 100644 --- a/src/tanstack-query.ts +++ b/src/tanstack-query.ts @@ -47,10 +47,10 @@ class TanstackQueryOptimisticHelpers { * Set the entire query data. * If the query doesn't exist, the new query is created. */ - set( + set = ( queryKey: QueryKeyAndFn, value: Data | ((prev: Data | undefined) => Data | undefined), - ) { + ) => { const prev = this.#get(queryKey); const newValue = typeof value === "function" @@ -68,16 +68,16 @@ class TanstackQueryOptimisticHelpers { this.#set(queryKey, prev); } }); - } + }; /** * Update the entire query data. * If the query doesn't exist, it cancels */ - updateExisting( + updateExisting = ( queryKey: QueryKeyAndFn, value: Data | ((prev: Data) => Data), - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; @@ -89,14 +89,14 @@ class TanstackQueryOptimisticHelpers { this.#onRestore(() => { this.#set(queryKey, prev); }); - } + }; /** * Mark a query as changed and schedule a refetch. * Does not modify any data. * If the query doesn't exist, the updater is skipped. */ - refetchOnSettled(queryKey: QueryKeyAndFn) { + refetchOnSettled = (queryKey: QueryKeyAndFn) => { if (!queryKey.queryFn) return; const state = this.#client.getQueryCache().find({ queryKey: queryKey.queryKey, @@ -109,19 +109,19 @@ class TanstackQueryOptimisticHelpers { await this.#client.invalidateQueries(queryKey); }); } catch { /* Ignore expiry */ } - } + }; /** * Set a property at an object path. * If the query or path doesn't exist, the updater is skipped. */ - objSet>( + objSet = >( queryKey: QueryKeyAndFn, path: Path, value: | Exclude, Function> | ((prev: GetObjectPath) => GetObjectPath), - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; const { value: original, exists } = getPath(prev, path); @@ -145,20 +145,20 @@ class TanstackQueryOptimisticHelpers { this.#onRestore(() => { this.#set(queryKey, (obj) => obj ? setPath(obj, path, original) : obj); }); - } + }; /** * Increment a numeric property at an object path. * If the query or path doesn't exist, the updater is skipped. */ - objIncrement< + objIncrement = < Data extends object, const Path extends AllObjectPaths, >( queryKey: QueryKeyAndFn, path: Path, amount: number = 1, - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; const { value: original, exists } = getPath(prev, path); @@ -171,20 +171,20 @@ class TanstackQueryOptimisticHelpers { this.#onRestore(() => { this.#set(queryKey, (obj) => obj ? setPath(obj, path, original) : obj); }); - } + }; /** * Decrement a numeric property at an object path. * If the query or path doesn't exist, the updater is skipped. */ - objDecrement< + objDecrement = < Data extends object, const Path extends AllObjectPaths, >( queryKey: QueryKeyAndFn, path: Path, amount: number = 1, - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; const { value: original, exists } = getPath(prev, path); @@ -197,16 +197,16 @@ class TanstackQueryOptimisticHelpers { this.#onRestore(() => { this.#set(queryKey, (obj) => obj ? setPath(obj, path, original) : obj); }); - } + }; /** * Toggle a boolean property at an object path. * If the query or path doesn't exist, the updater is skipped. */ - objToggle>( + objToggle = >( queryKey: QueryKeyAndFn, path: Path, - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; const { value: original, exists } = getPath(prev, path); @@ -219,17 +219,17 @@ class TanstackQueryOptimisticHelpers { this.#onRestore(() => { this.#set(queryKey, (obj) => obj ? setPath(obj, path, original) : obj); }); - } + }; /** * Shallow merge multiple properties at an object path. * If the query or path doesn't exist, the updater is skipped. */ - objSetMany>( + objSetMany = >( queryKey: QueryKeyAndFn, path: Path, updates: Partial>, - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; const { value: original, exists } = getPath(prev, path); @@ -248,13 +248,13 @@ class TanstackQueryOptimisticHelpers { this.#onRestore(() => { this.#set(queryKey, (obj) => obj ? setPath(obj, path, original) : obj); }); - } + }; /** * Push item(s) to the end of an array at an object path. * If the query or path doesn't exist, the updater is skipped. */ - objArrayPush< + objArrayPush = < Data extends object, const Path extends AllObjectPaths, >( @@ -262,7 +262,7 @@ class TanstackQueryOptimisticHelpers { path: Path, ...items: GetObjectPath extends Array ? T[] : never - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; const { value: original, exists } = getPath(prev, path); @@ -276,13 +276,13 @@ class TanstackQueryOptimisticHelpers { this.#onRestore(() => { this.#set(queryKey, (obj) => obj ? setPath(obj, path, original) : obj); }); - } + }; /** * Add item(s) to the beginning of an array at an object path. * If the query or path doesn't exist, the updater is skipped. */ - objArrayUnshift< + objArrayUnshift = < Data extends object, const Path extends AllObjectPaths, >( @@ -290,7 +290,7 @@ class TanstackQueryOptimisticHelpers { path: Path, ...items: GetObjectPath extends Array ? T[] : never - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; const { value: original, exists } = getPath(prev, path); @@ -304,13 +304,13 @@ class TanstackQueryOptimisticHelpers { this.#onRestore(() => { this.#set(queryKey, (obj) => obj ? setPath(obj, path, original) : obj); }); - } + }; /** * Remove items from an array that match `filter`. * If the query or path doesn't exist, the updater is skipped. */ - objArrayRemove< + objArrayRemove = < Data extends object, const Path extends AllObjectPaths, >( @@ -320,7 +320,7 @@ class TanstackQueryOptimisticHelpers { item: GetObjectPath extends Array ? T : never, index: number, ) => boolean, - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; const { value: original, exists } = getPath(prev, path); @@ -335,13 +335,13 @@ class TanstackQueryOptimisticHelpers { // TODO: splice items back in case original changed this.#set(queryKey, (obj) => obj ? setPath(obj, path, original) : obj); }); - } + }; /** * Filter items to just include items that match `filter`. This is the inverse of `objArrayRemove` * If the query or path doesn't exist, the updater is skipped. */ - objArrayFilter< + objArrayFilter = < Data extends object, const Path extends AllObjectPaths, >( @@ -351,7 +351,7 @@ class TanstackQueryOptimisticHelpers { item: GetObjectPath extends Array ? T : never, index: number, ) => boolean, - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; const { value: original, exists } = getPath(prev, path); @@ -366,13 +366,13 @@ class TanstackQueryOptimisticHelpers { // TODO: splice items back in case original changed this.#set(queryKey, (obj) => obj ? setPath(obj, path, original) : obj); }); - } + }; /** * Update items in an array that match a predicate. * If the query or path doesn't exist, the updater is skipped. */ - objArrayUpdate< + objArrayUpdate = < Data extends object, const Path extends AllObjectPaths, >( @@ -387,7 +387,7 @@ class TanstackQueryOptimisticHelpers { item: GetObjectPath extends Array ? T : never, ) => GetObjectPath extends Array ? T : never; }, - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; const { value: original, exists } = getPath(prev, path); @@ -402,13 +402,13 @@ class TanstackQueryOptimisticHelpers { // TODO: splice items back in case original changed this.#set(queryKey, (obj) => obj ? setPath(obj, path, original) : obj); }); - } + }; /** * Insert item(s) at a specific index in an array. * If the query or path doesn't exist, the updater is skipped. */ - objArrayInsertIndex< + objArrayInsertIndex = < Data extends object, const Path extends AllObjectPaths, >( @@ -417,7 +417,7 @@ class TanstackQueryOptimisticHelpers { index: number, ...items: GetObjectPath extends Array ? T[] : never - ) { + ) => { const prev = this.#get(queryKey); if (!prev) return; const { value: original, exists } = getPath(prev, path); @@ -436,13 +436,13 @@ class TanstackQueryOptimisticHelpers { // TODO: splice items back in case original changed this.#set(queryKey, (obj) => obj ? setPath(obj, path, original) : obj); }); - } + }; /** * Push item(s) to the end of an array at an object path. * If the query or path doesn't exist, the updater is skipped. */ - arrayPush(queryKey: QueryKeyAndFn, ...items: Data[]) { + arrayPush = (queryKey: QueryKeyAndFn, ...items: Data[]) => { const prev = this.#get(queryKey); if (!prev || !Array.isArray(prev)) return; @@ -454,13 +454,13 @@ class TanstackQueryOptimisticHelpers { (old) => old ? old.filter((x) => !items.includes(x)) : old, ); }); - } + }; /** * Add item(s) to the beginning of an array at an object path. * If the query or path doesn't exist, the updater is skipped. */ - arrayUnshift(queryKey: QueryKeyAndFn, ...items: Data[]) { + arrayUnshift = (queryKey: QueryKeyAndFn, ...items: Data[]) => { const prev = this.#get(queryKey); if (!prev || !Array.isArray(prev)) return; @@ -472,19 +472,19 @@ class TanstackQueryOptimisticHelpers { (old) => old ? old.filter((x) => !items.includes(x)) : old, ); }); - } + }; /** * Remove items from an array that match a `filter`. * If the query or path doesn't exist, the updater is skipped. */ - arrayRemove( + arrayRemove = ( queryKey: QueryKeyAndFn, filter: ( item: Data, index: number, ) => boolean, - ) { + ) => { const prev = this.#get(queryKey); if (!prev || !Array.isArray(prev)) return; @@ -494,19 +494,19 @@ class TanstackQueryOptimisticHelpers { // TODO: splice items back in case original changed this.#set(queryKey, prev); }); - } + }; /** * Filter items to just include items that match `filter`. This is the inverse of `arrayRemove` * If the query or path doesn't exist, the updater is skipped. */ - arrayFilter( + arrayFilter = ( queryKey: QueryKeyAndFn, filter: ( item: Data, index: number, ) => boolean, - ) { + ) => { const prev = this.#get(queryKey); if (!prev || !Array.isArray(prev)) return; @@ -516,13 +516,13 @@ class TanstackQueryOptimisticHelpers { // TODO: splice items back in case original changed this.#set(queryKey, prev); }); - } + }; /** * Update items in an array that match a `filter`. * If the query or path doesn't exist, the updater is skipped. */ - arrayUpdate( + arrayUpdate = ( queryKey: QueryKeyAndFn, { filter, @@ -531,7 +531,7 @@ class TanstackQueryOptimisticHelpers { filter?: (item: Data, index: number) => boolean; update: (item: Data) => Data; }, - ) { + ) => { const prev = this.#get(queryKey); if (!prev || !Array.isArray(prev)) return; @@ -541,17 +541,17 @@ class TanstackQueryOptimisticHelpers { // TODO: splice items back in case original changed this.#set(queryKey, prev); }); - } + }; /** * Insert item(s) at a specific index in an array. * If the query or path doesn't exist, the updater is skipped. */ - arrayInsertIndex( + arrayInsertIndex = ( queryKey: QueryKeyAndFn, index: number, ...items: Data[] - ) { + ) => { const prev = this.#get(queryKey); if (!prev || !Array.isArray(prev)) return; @@ -565,12 +565,12 @@ class TanstackQueryOptimisticHelpers { // TODO: splice items back in case original changed this.#set(queryKey, prev); }); - } + }; /** * Remove a query from the cache entirely. */ - removeQuery(queryKey: QueryKeyAndFn) { + removeQuery = (queryKey: QueryKeyAndFn) => { const prev = this.#get(queryKey); if (!prev) return; @@ -578,7 +578,7 @@ class TanstackQueryOptimisticHelpers { this.#onRestore(() => { this.#set(queryKey, prev); }); - } + }; } export function queryClientOptimisticHelpers( diff --git a/test/tanstack-query-helpers.test.ts b/test/tanstack-query-helpers.test.ts index e4bff6b103aca0388fafd58b2c9bc4dba7a66312..0ea3486d5b520a7411144c409aefd6b0b47fce89 100644 --- a/test/tanstack-query-helpers.test.ts +++ b/test/tanstack-query-helpers.test.ts @@ -46,6 +46,23 @@ function createTestQueryClient() { return { client, queryTest }; } +test("helpers can be spread and retain bound this", () => { + const { client, queryTest } = createTestQueryClient(); + + const helpers = queryClientOptimisticHelpers(client)({ + onRestore: () => {}, + onRefetch: () => {}, + }); + const spreadHelpers = { ...helpers }; + + assertEquals(Object.keys(spreadHelpers).includes("objIncrement"), true); + + spreadHelpers.objIncrement(queryTest, ["count"], 2); + + const result = client.getQueryData(queryTest.queryKey); + assertEquals(result?.count, 12); +}); + // ============================================================================ // set() tests // ============================================================================