authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-29 16:56:46-08:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-29 16:57:23-08:00
logd5c11b294ef2257d31778609caddf9980e24e6f6
tree19754a39aaf32be7470df76e19359f05f95a5093
parentb51077c59854543b2c70ea2be825d68cfb567d8c
signaturelock-open Commit is signed but in an unrecognized format.

feat: arrayFilter and useMutate args


3 files changed, 36 insertions(+), 1 deletions(-)

jsr.json+1-1
......@@ -1,6 +1,6 @@
11{
22 "name": "@clo/react-mutation",
3 "version": "1.0.0-beta.7",
3 "version": "1.0.0-beta.8",
44 "exports": {
55 ".": "./src/mod.ts",
66 "./tanstack-query.ts": "./src/tanstack-query.ts",
src/react.ts+13
......@@ -45,6 +45,7 @@ export interface UseMutateResultBase<Args extends unknown[], Result> {
4545 ..._: [...args: Args, options: RunOptions<Result>]
4646 ) => Promise<Result>;
4747 clear: () => void;
48 args: Args | undefined;
4849}
4950
5051export interface UseMutateSuccess<Result> {
......@@ -175,7 +176,11 @@ class Observer<Args extends unknown[], Result> {
175176 run(...args: Args) {
176177 const mutation = this.mutation;
177178 if (!mutation) return;
179 const argsChanged = this.currentArgs !== args;
178180 this.currentArgs = args;
181 if (argsChanged && this.watched.has("args")) {
182 this.setRerender(Math.random());
183 }
179184 const key = mutation.key(args);
180185 if (key !== this.currentKey) {
181186 this.currentKey = key;
......@@ -249,7 +254,11 @@ class Observer<Args extends unknown[], Result> {
249254 const args = array.slice() as Args;
250255 const options = args.pop() as RunOptions<Result>;
251256
257 const argsChanged = this.currentArgs !== args;
252258 this.currentArgs = args;
259 if (argsChanged && this.watched.has("args")) {
260 this.setRerender(Math.random());
261 }
253262 const key = mutation.key(args);
254263
255264 // Set up subscription if key changed
......@@ -347,6 +356,10 @@ class Observer<Args extends unknown[], Result> {
347356 self.watched.add("isOptimisticData");
348357 return self.state.isOptimisticData;
349358 },
359 get args() {
360 self.watched.add("args");
361 return self.currentArgs ?? undefined;
362 },
350363 } as UseMutateResult<Args, Result>))(this);
351364}
352365
src/tanstack-query.ts+22
......@@ -509,6 +509,28 @@ class TanstackQueryOptimisticHelpers {
509509 });
510510 }
511511
512 /**
513 * Filter items to just include items that match `filter`. This is the inverse of `arrayRemove`
514 * If the query or path doesn't exist, the updater is skipped.
515 */
516 arrayFilter<Data>(
517 queryKey: QueryKeyAndFn<Data[] | null>,
518 filter: (
519 item: Data,
520 index: number,
521 ) => boolean,
522 ) {
523 const prev = this.#get(queryKey);
524 if (!prev || !Array.isArray(prev)) return;
525
526 const newArray = prev.filter(filter);
527 this.#set(queryKey, newArray);
528 this.#onRestore(() => {
529 // TODO: splice items back in case original changed
530 this.#set(queryKey, prev);
531 });
532 }
533
512534 /**
513535 * Update items in an array that match a `filter`.
514536 * If the query or path doesn't exist, the updater is skipped.