authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-30 17:59:52-08:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-30 18:00:25-08:00
loge3d6a4b906d60a10abef5cb9611947b52c979f1a
tree4a4ae27a2c0615f53649fa20b4079d18c781b904
parent9fe44b404d1cfae5e4751ac3ef020ef8da521b1a
signaturebadge-check Signed by SSH key SHA256:xbd+BjjhyBfwk7GVoURf9Yx0gzDerHbvYv7SddNWmAs

feat!: disambiguate "data only"


7 files changed, 25 insertions(+), 15 deletions(-)

jsr.json+1-1
......@@ -1,6 +1,6 @@
11{
22 "name": "@clo/react-mutation",
3 "version": "1.1.0",
3 "version": "2.0.0",
44 "exports": {
55 ".": "./src/mod.ts",
66 "./tanstack-query.ts": "./src/tanstack-query.ts",
readme.changes.md+6
......@@ -1,5 +1,11 @@
11# notable changes in React Mutation
22
3## v2
4
5### breaking
6
7- rename `onSuccess` and `onSuccessDataOnly` to `onSuccessUi` and `onSuccessData`.
8
39## v1.1
410
511- Add `runAsHeadlessPromise`. The function name is intentionally long to avoid using it, please use `runWithOptions` instead.
src/mutation.ts+7-7
......@@ -273,13 +273,13 @@ export class BlockingMutation<
273273 }
274274
275275 const args = array.slice() as Args;
276 const { onSuccess, onSuccessDataOnly, onError, onSettled, onRestore } = args
276 const { onSuccessUi: onSuccess, onSuccessData, onError, onSettled, onRestore } = args
277277 .pop() as RunOptions<Result>;
278278 const promise = this.#runWithOptions(args, onRestore, true);
279279 return promise.then((result) => {
280280 // Call user handlers
281281 onSuccess?.(result);
282 onSuccessDataOnly?.(result);
282 onSuccessData?.(result);
283283 onSettled?.({ status: "success", result });
284284 return result;
285285 }).catch((caught: unknown) => {
......@@ -305,17 +305,17 @@ export class BlockingMutation<
305305 }
306306
307307 const args = array.slice() as Args;
308 const { onSuccess, onSuccessDataOnly, onError, onSettled, onRestore } = args
308 const { onSuccessUi, onSuccessData, onError, onSettled, onRestore } = args
309309 .pop() as RunOptions<Result>;
310 const suppressAll = this.#options.debounceMs !== undefined && !onSuccess && !onError;
311 const suppressGlobalSuccess = onSuccess !== undefined || suppressAll;
310 const suppressAll = this.#options.debounceMs !== undefined && !onSuccessUi && !onError;
311 const suppressGlobalSuccess = onSuccessUi !== undefined || suppressAll;
312312 const suppressGlobalError = onError !== undefined || suppressAll;
313313
314314 const promise = this.#runWithOptions(args, onRestore, suppressAll);
315315 promise.then((result) => {
316316 // Call user handlers
317 onSuccess?.(result);
318 onSuccessDataOnly?.(result);
317 onSuccessUi?.(result);
318 onSuccessData?.(result);
319319 onSettled?.({ status: "success", result });
320320
321321 // Call global handler unless suppressed
src/react.ts+1-1
......@@ -261,7 +261,7 @@ class Observer<Args extends unknown[], Result> {
261261 const promise = mutation.runWithOptions(
262262 ...args,
263263 {
264 onSuccess: watchesSuccess ? () => {} : undefined,
264 onSuccessUi: watchesSuccess ? () => {} : undefined,
265265 onError: watchesError ? () => {} : undefined,
266266 // For debounced mutations, suppress global handlers in runWithOptions
267267 // The debounce logic (#enqueueDebouncedCall) will call them once if needed
src/types.ts+8-4
......@@ -29,10 +29,14 @@ export interface Mutation<Args extends unknown[], Result> {
2929}
3030
3131export interface RunOptions<Result> {
32 /** Called on success, suppresses the global success handler */
33 onSuccess?: (result: Result) => void;
34 /** Called on success, does NOT suppress the global success handler */
35 onSuccessDataOnly?: (result: Result) => void;
32 /** Called to show the success UI. Passing this suppresses the global success handler. */
33 onSuccessUi?: (result: Result) => void;
34 /**
35 * Called with result data, but unlike `onSuccessUi`, this indicates the
36 * caller is not concerned with the UI flow of the success. Passing this
37 * does NOT suppress the global success handler.
38 */
39 onSuccessData?: (result: Result) => void;
3640 /** Called on error, suppresses the global error handler */
3741 onError?: (error: unknown) => void;
3842 /** Called on settled (doesn't suppress global handlers) */
test/ordering.test.ts+1-1
......@@ -378,7 +378,7 @@ test("runWithOptions callbacks: onSuccess called before global handler", async (
378378 });
379379
380380 mutTest.runWithOptions({
381 onSuccess: () => {
381 onSuccessUi: () => {
382382 calls.push("onSuccess");
383383 },
384384 onSettled: () => {
test/runWithOptions.test.tsx+1-1
......@@ -32,7 +32,7 @@ test("runWithOptions should allow react hook to do local handling", async () =>
3232 <button
3333 data-testid="a"
3434 onClick={() => {
35 runWithOptions({ onSuccessDataOnly: () => {} });
35 runWithOptions({ onSuccessData: () => {} });
3636 }}
3737 >
3838 button