@boxcustodia/library 2.0.0-alpha.19 → 2.0.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/button/button.cjs.js +1 -1
- package/dist/components/button/button.es.js +19 -18
- package/dist/components/button/components/base-button.cjs.js +1 -1
- package/dist/components/button/components/base-button.es.js +20 -20
- package/dist/components/calendar/calendar.cjs.js +1 -1
- package/dist/components/calendar/calendar.es.js +1 -0
- package/dist/components/date-picker/date-input.cjs.js +1 -1
- package/dist/components/date-picker/date-input.es.js +92 -75
- package/dist/components/date-picker/date-picker.cjs.js +1 -1
- package/dist/components/date-picker/date-picker.es.js +104 -95
- package/dist/components/date-picker/date-picker.utils.cjs.js +1 -1
- package/dist/components/date-picker/date-picker.utils.es.js +51 -43
- package/dist/components/date-picker/use-hidden-field-value.cjs.js +1 -0
- package/dist/components/date-picker/use-hidden-field-value.es.js +11 -0
- package/dist/components/menu/menu.es.js +1 -9
- package/dist/components/otp/otp.cjs.js +2 -0
- package/dist/components/otp/otp.es.js +93 -0
- package/dist/components/password/password.cjs.js +1 -1
- package/dist/components/password/password.es.js +2 -2
- package/dist/components/select/select.cjs.js +1 -1
- package/dist/components/select/select.es.js +68 -60
- package/dist/hooks/internal/is-apple-device.cjs.js +1 -0
- package/dist/hooks/internal/is-apple-device.es.js +9 -0
- package/dist/hooks/internal/use-latest-ref.cjs.js +1 -0
- package/dist/hooks/internal/use-latest-ref.es.js +11 -0
- package/dist/hooks/use-array/use-array.cjs.js +1 -1
- package/dist/hooks/use-array/use-array.es.js +54 -42
- package/dist/hooks/use-async/use-async.cjs.js +1 -1
- package/dist/hooks/use-async/use-async.es.js +53 -20
- package/dist/hooks/use-boolean/use-boolean.cjs.js +1 -0
- package/dist/hooks/use-boolean/use-boolean.es.js +25 -0
- package/dist/hooks/use-click-outside/use-click-outside.cjs.js +1 -1
- package/dist/hooks/use-click-outside/use-click-outside.es.js +26 -12
- package/dist/hooks/use-debounce-callback/use-debounced-callback.cjs.js +1 -1
- package/dist/hooks/use-debounce-callback/use-debounced-callback.es.js +27 -10
- package/dist/hooks/use-debounce-value/use-debounced-value.cjs.js +1 -1
- package/dist/hooks/use-debounce-value/use-debounced-value.es.js +7 -9
- package/dist/hooks/use-disclosure/use-disclosure.cjs.js +1 -1
- package/dist/hooks/use-disclosure/use-disclosure.es.js +21 -11
- package/dist/hooks/use-document-title/use-document-title.cjs.js +1 -1
- package/dist/hooks/use-document-title/use-document-title.es.js +14 -12
- package/dist/hooks/use-event-listener/use-event-listener.cjs.js +1 -1
- package/dist/hooks/use-event-listener/use-event-listener.es.js +17 -9
- package/dist/hooks/use-hotkey/use-hotkey.cjs.js +1 -1
- package/dist/hooks/use-hotkey/use-hotkey.es.js +30 -14
- package/dist/hooks/use-hotkey/utils/is-input-field.cjs.js +1 -1
- package/dist/hooks/use-hotkey/utils/is-input-field.es.js +4 -2
- package/dist/hooks/use-hotkey/utils/match-and-run.cjs.js +1 -0
- package/dist/hooks/use-hotkey/utils/match-and-run.es.js +12 -0
- package/dist/hooks/use-hotkey/utils/match-key-modifiers.cjs.js +1 -1
- package/dist/hooks/use-hotkey/utils/match-key-modifiers.es.js +13 -12
- package/dist/hooks/use-hover/use-hover.cjs.js +1 -1
- package/dist/hooks/use-hover/use-hover.es.js +32 -17
- package/dist/hooks/use-is-visible/use-is-visible.cjs.js +1 -1
- package/dist/hooks/use-is-visible/use-is-visible.es.js +31 -27
- package/dist/hooks/use-local-storage/use-local-storage.cjs.js +1 -1
- package/dist/hooks/use-local-storage/use-local-storage.es.js +52 -20
- package/dist/hooks/use-media-query/use-media-query.cjs.js +1 -1
- package/dist/hooks/use-media-query/use-media-query.es.js +21 -11
- package/dist/hooks/use-mutation/use-mutation.cjs.js +1 -1
- package/dist/hooks/use-mutation/use-mutation.es.js +36 -22
- package/dist/hooks/use-object/use-object.cjs.js +1 -1
- package/dist/hooks/use-object/use-object.es.js +26 -22
- package/dist/hooks/use-prevent-page-close/use-prevent-page-close.cjs.js +1 -0
- package/dist/hooks/use-prevent-page-close/use-prevent-page-close.es.js +14 -0
- package/dist/hooks/use-step/use-step.cjs.js +1 -1
- package/dist/hooks/use-step/use-step.es.js +25 -24
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +308 -300
- package/dist/src/components/date-picker/date-picker.utils.d.ts +17 -0
- package/dist/src/components/date-picker/use-hidden-field-value.d.ts +12 -0
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/hooks/index.d.ts +2 -2
- package/dist/src/hooks/internal/index.d.ts +2 -0
- package/dist/src/hooks/internal/is-apple-device.d.ts +12 -0
- package/dist/src/hooks/internal/use-latest-ref.d.ts +12 -0
- package/dist/src/hooks/use-array/use-array.d.ts +24 -11
- package/dist/src/hooks/use-async/use-async.d.ts +16 -13
- package/dist/src/hooks/use-boolean/index.d.ts +1 -0
- package/dist/src/hooks/use-boolean/use-boolean.d.ts +15 -0
- package/dist/src/hooks/use-boolean/use-boolean.test.d.ts +1 -0
- package/dist/src/hooks/use-click-outside/use-click-outside.d.ts +23 -1
- package/dist/src/hooks/use-debounce-callback/use-debounced-callback.d.ts +19 -1
- package/dist/src/hooks/use-debounce-value/use-debounced-value.d.ts +10 -1
- package/dist/src/hooks/use-disclosure/use-disclosure.d.ts +17 -8
- package/dist/src/hooks/use-document-title/use-document-title.d.ts +11 -0
- package/dist/src/hooks/use-event-listener/use-event-listener.d.ts +18 -1
- package/dist/src/hooks/use-hotkey/index.d.ts +2 -1
- package/dist/src/hooks/use-hotkey/use-hotkey.d.ts +62 -5
- package/dist/src/hooks/use-hotkey/utils/index.d.ts +4 -3
- package/dist/src/hooks/use-hotkey/utils/is-input-field.d.ts +12 -2
- package/dist/src/hooks/use-hotkey/utils/is-input-field.test.d.ts +1 -0
- package/dist/src/hooks/use-hotkey/utils/match-and-run.d.ts +36 -0
- package/dist/src/hooks/use-hotkey/utils/match-and-run.test.d.ts +1 -0
- package/dist/src/hooks/use-hotkey/utils/match-key-modifiers.d.ts +20 -6
- package/dist/src/hooks/use-hotkey/utils/match-key-modifiers.test.d.ts +1 -0
- package/dist/src/hooks/use-hover/use-hover.d.ts +8 -4
- package/dist/src/hooks/use-is-visible/use-is-visible.d.ts +28 -4
- package/dist/src/hooks/use-local-storage/use-local-storage.d.ts +13 -2
- package/dist/src/hooks/use-media-query/use-media-query.d.ts +10 -1
- package/dist/src/hooks/use-media-query/use-media-query.test.d.ts +1 -0
- package/dist/src/hooks/use-mutation/use-mutation.d.ts +18 -11
- package/dist/src/hooks/use-object/use-object.d.ts +15 -6
- package/dist/src/hooks/use-prevent-page-close/index.d.ts +1 -0
- package/dist/src/hooks/use-prevent-page-close/use-prevent-page-close.d.ts +10 -0
- package/dist/src/hooks/use-prevent-page-close/use-prevent-page-close.test.d.ts +1 -0
- package/dist/src/hooks/use-step/use-step.d.ts +18 -11
- package/dist/src/utils/form.d.ts +10 -0
- package/package.json +1 -1
- package/src/components/alert-dialog/alert-dialog.test.tsx +13 -9
- package/src/components/auto-complete/auto-complete.test.tsx +4 -14
- package/src/components/avatar/avatar.test.tsx +7 -12
- package/src/components/button/button.test.tsx +10 -15
- package/src/components/button/button.tsx +14 -9
- package/src/components/button/components/base-button.tsx +2 -4
- package/src/components/calendar/calendar.test.tsx +12 -19
- package/src/components/calendar/calendar.tsx +4 -0
- package/src/components/card/card.test.tsx +4 -6
- package/src/components/checkbox/checkbox.test.tsx +12 -8
- package/src/components/checkbox-group/checkbox-group.test.tsx +7 -8
- package/src/components/combobox/combobox.test.tsx +24 -21
- package/src/components/date-picker/date-input-form.test.tsx +77 -0
- package/src/components/date-picker/date-input.stories.tsx +30 -18
- package/src/components/date-picker/date-input.tsx +77 -44
- package/src/components/date-picker/date-picker.stories.tsx +31 -1
- package/src/components/date-picker/date-picker.test.tsx +3 -13
- package/src/components/date-picker/date-picker.tsx +35 -16
- package/src/components/date-picker/date-picker.utils.test.ts +32 -14
- package/src/components/date-picker/date-picker.utils.ts +33 -0
- package/src/components/date-picker/use-date-input-popover.test.ts +3 -1
- package/src/components/date-picker/use-hidden-field-value.ts +23 -0
- package/src/components/dialog/dialog.test.tsx +10 -8
- package/src/components/dropzone/dropzone.test.tsx +11 -13
- package/src/components/empty/empty.test.tsx +4 -3
- package/src/components/field/field.test.tsx +12 -13
- package/src/components/form/form.stories.tsx +16 -1
- package/src/components/index.ts +1 -0
- package/src/components/label/label.test.tsx +3 -3
- package/src/components/menu/menu.tsx +1 -5
- package/src/components/number-input/number-input.test.tsx +6 -2
- package/src/components/password/password.test.tsx +20 -6
- package/src/components/password/password.tsx +2 -2
- package/src/components/popover/popover.test.tsx +4 -4
- package/src/components/progress/progress.test.tsx +7 -8
- package/src/components/radio-group/radio-group.test.tsx +17 -11
- package/src/components/select/select.test.tsx +10 -10
- package/src/components/select/select.tsx +9 -1
- package/src/components/stepper/stepper.stories.tsx +11 -15
- package/src/components/stepper/stepper.test.tsx +6 -4
- package/src/components/switch/switch.test.tsx +3 -3
- package/src/components/table/table.test.tsx +9 -3
- package/src/components/tabs/tabs.test.tsx +6 -2
- package/src/components/tag/tag.test.tsx +1 -3
- package/src/components/textarea/textarea.test.tsx +4 -1
- package/src/components/timeline/timeline.test.tsx +10 -5
- package/src/components/toast/toast.test.tsx +11 -14
- package/src/components/tooltip/tooltip.test.tsx +1 -5
- package/src/components/tree/tree.test.tsx +3 -1
- package/src/hooks/index.ts +2 -2
- package/src/hooks/internal/index.ts +2 -0
- package/src/hooks/internal/is-apple-device.test.ts +41 -0
- package/src/hooks/internal/is-apple-device.ts +33 -0
- package/src/hooks/internal/use-isomorphic-layout-effect.ts +3 -1
- package/src/hooks/internal/use-latest-ref.ts +21 -0
- package/src/hooks/use-array/use-array.stories.tsx +435 -64
- package/src/hooks/use-array/use-array.test.tsx +398 -15
- package/src/hooks/use-array/use-array.ts +105 -66
- package/src/hooks/use-async/use-async.stories.tsx +255 -131
- package/src/hooks/use-async/use-async.test.ts +397 -0
- package/src/hooks/use-async/use-async.ts +117 -39
- package/src/hooks/use-boolean/index.ts +1 -0
- package/src/hooks/use-boolean/use-boolean.stories.tsx +377 -0
- package/src/hooks/use-boolean/use-boolean.test.tsx +177 -0
- package/src/hooks/use-boolean/use-boolean.ts +50 -0
- package/src/hooks/use-click-outside/use-click-outside.stories.tsx +188 -18
- package/src/hooks/use-click-outside/use-click-outside.test.tsx +89 -10
- package/src/hooks/use-click-outside/use-click-outside.ts +62 -16
- package/src/hooks/use-debounce-callback/use-debounced-callback.stories.tsx +141 -41
- package/src/hooks/use-debounce-callback/use-debounced-callback.test.ts +217 -9
- package/src/hooks/use-debounce-callback/use-debounced-callback.ts +71 -11
- package/src/hooks/use-debounce-value/use-debounced-value.stories.tsx +247 -47
- package/src/hooks/use-debounce-value/use-debounced-value.test.ts +105 -10
- package/src/hooks/use-debounce-value/use-debounced-value.ts +19 -10
- package/src/hooks/use-disclosure/use-disclosure.stories.tsx +305 -14
- package/src/hooks/use-disclosure/use-disclosure.test.ts +198 -50
- package/src/hooks/use-disclosure/use-disclosure.ts +49 -29
- package/src/hooks/use-document-title/use-document-title.stories.tsx +54 -0
- package/src/hooks/use-document-title/use-document-title.test.tsx +26 -0
- package/src/hooks/use-document-title/{use-document-title.tsx → use-document-title.ts} +17 -3
- package/src/hooks/use-event-listener/use-event-listener.stories.tsx +105 -9
- package/src/hooks/use-event-listener/use-event-listener.test.tsx +77 -10
- package/src/hooks/use-event-listener/use-event-listener.ts +71 -11
- package/src/hooks/use-focus-trap/use-focus-trap.test.ts +31 -6
- package/src/hooks/use-focus-trap/use-focus-trap.ts +3 -2
- package/src/hooks/use-hotkey/index.ts +9 -1
- package/src/hooks/use-hotkey/use-hotkey.stories.tsx +279 -74
- package/src/hooks/use-hotkey/use-hotkey.test.tsx +286 -34
- package/src/hooks/use-hotkey/use-hotkey.ts +141 -17
- package/src/hooks/use-hotkey/utils/index.ts +8 -3
- package/src/hooks/use-hotkey/utils/is-input-field.test.ts +78 -0
- package/src/hooks/use-hotkey/utils/is-input-field.ts +31 -10
- package/src/hooks/use-hotkey/utils/match-and-run.test.ts +203 -0
- package/src/hooks/use-hotkey/utils/match-and-run.ts +62 -0
- package/src/hooks/use-hotkey/utils/match-key-modifiers.test.ts +65 -0
- package/src/hooks/use-hotkey/utils/match-key-modifiers.ts +39 -12
- package/src/hooks/use-hover/use-hover.stories.tsx +258 -80
- package/src/hooks/use-hover/use-hover.test.tsx +266 -26
- package/src/hooks/use-hover/use-hover.tsx +93 -28
- package/src/hooks/use-is-visible/use-is-visible.stories.tsx +193 -46
- package/src/hooks/use-is-visible/use-is-visible.test.tsx +235 -7
- package/src/hooks/use-is-visible/use-is-visible.ts +114 -0
- package/src/hooks/use-local-storage/use-local-storage.stories.tsx +129 -29
- package/src/hooks/use-local-storage/use-local-storage.test.ts +106 -41
- package/src/hooks/use-local-storage/use-local-storage.ts +100 -31
- package/src/hooks/use-media-query/use-media-query.stories.tsx +86 -26
- package/src/hooks/use-media-query/use-media-query.test.ts +132 -0
- package/src/hooks/use-media-query/use-media-query.ts +39 -14
- package/src/hooks/use-memoized-fn/use-memoized-fn.ts +0 -1
- package/src/hooks/use-mutation/use-mutation.stories.tsx +260 -94
- package/src/hooks/use-mutation/use-mutation.test.ts +359 -0
- package/src/hooks/use-mutation/use-mutation.ts +97 -0
- package/src/hooks/use-object/use-object.stories.tsx +310 -79
- package/src/hooks/use-object/use-object.test.tsx +235 -56
- package/src/hooks/use-object/use-object.ts +59 -0
- package/src/hooks/use-pagination/use-pagination.tsx +0 -1
- package/src/hooks/use-prevent-page-close/index.ts +1 -0
- package/src/hooks/use-prevent-page-close/use-prevent-page-close.stories.tsx +39 -0
- package/src/hooks/use-prevent-page-close/use-prevent-page-close.test.ts +89 -0
- package/src/hooks/use-prevent-page-close/use-prevent-page-close.ts +27 -0
- package/src/hooks/use-range-pagination/use-range-pagination.test.tsx +1 -1
- package/src/hooks/use-range-pagination/use-range-pagination.tsx +1 -1
- package/src/hooks/use-selection/use-selection.ts +0 -1
- package/src/hooks/use-step/use-step.stories.tsx +178 -65
- package/src/hooks/use-step/use-step.test.ts +178 -53
- package/src/hooks/use-step/use-step.ts +57 -49
- package/src/utils/form.test.tsx +13 -8
- package/src/utils/form.tsx +10 -0
- package/src/utils/functions/getFormData.test.ts +1 -1
- package/dist/hooks/use-hotkey/utils/create-hotkey-listener.cjs.js +0 -1
- package/dist/hooks/use-hotkey/utils/create-hotkey-listener.es.js +0 -10
- package/dist/hooks/use-prevent-close-window/use-prevent-close-window.cjs.js +0 -1
- package/dist/hooks/use-prevent-close-window/use-prevent-close-window.es.js +0 -15
- package/dist/hooks/use-toggle/use-toggle.cjs.js +0 -1
- package/dist/hooks/use-toggle/use-toggle.es.js +0 -10
- package/dist/src/hooks/use-hotkey/utils/create-hotkey-listener.d.ts +0 -1
- package/dist/src/hooks/use-prevent-close-window/index.d.ts +0 -1
- package/dist/src/hooks/use-prevent-close-window/use-prevent-close-window.d.ts +0 -13
- package/dist/src/hooks/use-toggle/index.d.ts +0 -1
- package/dist/src/hooks/use-toggle/use-toggle.d.ts +0 -3
- package/src/hooks/use-async/use-async.test.tsx +0 -68
- package/src/hooks/use-hotkey/utils/create-hotkey-listener.ts +0 -25
- package/src/hooks/use-is-visible/use-is-visible.tsx +0 -49
- package/src/hooks/use-mutation/use-mutation.test.tsx +0 -83
- package/src/hooks/use-mutation/use-mutation.tsx +0 -59
- package/src/hooks/use-object/use-object.tsx +0 -46
- package/src/hooks/use-prevent-close-window/index.ts +0 -1
- package/src/hooks/use-prevent-close-window/use-prevent-close-window.stories.tsx +0 -32
- package/src/hooks/use-prevent-close-window/use-prevent-close-window.test.ts +0 -79
- package/src/hooks/use-prevent-close-window/use-prevent-close-window.ts +0 -33
- package/src/hooks/use-toggle/index.ts +0 -1
- package/src/hooks/use-toggle/use-toggle.stories.tsx +0 -25
- package/src/hooks/use-toggle/use-toggle.test.tsx +0 -64
- package/src/hooks/use-toggle/use-toggle.ts +0 -14
- /package/dist/src/{hooks/use-prevent-close-window/use-prevent-close-window.test.d.ts → components/date-picker/date-input-form.test.d.ts} +0 -0
- /package/dist/src/hooks/{use-toggle/use-toggle.test.d.ts → internal/is-apple-device.test.d.ts} +0 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/// <reference types="vitest/globals" />
|
|
2
|
+
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { useAsync } from "./use-async";
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Helpers
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
/** Creates a promise that can be resolved/rejected externally. */
|
|
11
|
+
function deferred<T>() {
|
|
12
|
+
let resolve!: (value: T) => void;
|
|
13
|
+
let reject!: (reason?: unknown) => void;
|
|
14
|
+
const promise = new Promise<T>((res, rej) => {
|
|
15
|
+
resolve = res;
|
|
16
|
+
reject = rej;
|
|
17
|
+
});
|
|
18
|
+
return { promise, resolve, reject };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Test 1 — enabled: false → idle initial state, fn never called
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
describe("useAsync", () => {
|
|
25
|
+
it("test 1: enabled: false → idle state, fn never called", () => {
|
|
26
|
+
const fn = vi.fn().mockResolvedValue("data");
|
|
27
|
+
const { result } = renderHook(() => useAsync({ fn, enabled: false }));
|
|
28
|
+
|
|
29
|
+
expect(result.current.status).toBe("idle");
|
|
30
|
+
expect(result.current.loading).toBe(false);
|
|
31
|
+
expect(result.current.data).toBeNull();
|
|
32
|
+
expect(result.current.error).toBeNull();
|
|
33
|
+
expect(fn).not.toHaveBeenCalled();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// -------------------------------------------------------------------------
|
|
37
|
+
// Test 2 — enabled: true (default) → initial status "pending", loading true
|
|
38
|
+
// -------------------------------------------------------------------------
|
|
39
|
+
it("test 2: enabled: true → initial status pending, loading true", () => {
|
|
40
|
+
const { promise } = deferred<string>();
|
|
41
|
+
const fn = vi.fn().mockReturnValue(promise);
|
|
42
|
+
const { result } = renderHook(() => useAsync({ fn }));
|
|
43
|
+
|
|
44
|
+
expect(result.current.status).toBe("pending");
|
|
45
|
+
expect(result.current.loading).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// -------------------------------------------------------------------------
|
|
49
|
+
// Test 3 — success path
|
|
50
|
+
// -------------------------------------------------------------------------
|
|
51
|
+
it("test 3: success path — data set, status success, loading false", async () => {
|
|
52
|
+
const fn = vi.fn().mockResolvedValue(42);
|
|
53
|
+
const { result } = renderHook(() => useAsync({ fn }));
|
|
54
|
+
|
|
55
|
+
await waitFor(() => expect(result.current.status).toBe("success"));
|
|
56
|
+
expect(result.current.data).toBe(42);
|
|
57
|
+
expect(result.current.error).toBeNull();
|
|
58
|
+
expect(result.current.loading).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// -------------------------------------------------------------------------
|
|
62
|
+
// Test 4 — error path
|
|
63
|
+
// -------------------------------------------------------------------------
|
|
64
|
+
it("test 4: error path — error set, status error, loading false", async () => {
|
|
65
|
+
const fn = vi.fn().mockRejectedValue(new Error("fail"));
|
|
66
|
+
const { result } = renderHook(() => useAsync({ fn }));
|
|
67
|
+
|
|
68
|
+
await waitFor(() => expect(result.current.status).toBe("error"));
|
|
69
|
+
expect(result.current.error?.message).toBe("fail");
|
|
70
|
+
expect(result.current.data).toBeNull();
|
|
71
|
+
expect(result.current.loading).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// -------------------------------------------------------------------------
|
|
75
|
+
// Test 5 — onSuccess fires with resolved data
|
|
76
|
+
// -------------------------------------------------------------------------
|
|
77
|
+
it("test 5: onSuccess callback fires with resolved data", async () => {
|
|
78
|
+
const onSuccess = vi.fn();
|
|
79
|
+
const fn = vi.fn().mockResolvedValue("hello");
|
|
80
|
+
renderHook(() => useAsync({ fn, onSuccess }));
|
|
81
|
+
|
|
82
|
+
await waitFor(() => expect(onSuccess).toHaveBeenCalledWith("hello"));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// -------------------------------------------------------------------------
|
|
86
|
+
// Test 6 — onError fires with rejection reason
|
|
87
|
+
// -------------------------------------------------------------------------
|
|
88
|
+
it("test 6: onError callback fires with rejection reason", async () => {
|
|
89
|
+
const onError = vi.fn();
|
|
90
|
+
const fn = vi.fn().mockRejectedValue(new Error("boom"));
|
|
91
|
+
renderHook(() => useAsync({ fn, onError }));
|
|
92
|
+
|
|
93
|
+
await waitFor(() =>
|
|
94
|
+
expect(onError).toHaveBeenCalledWith(expect.any(Error)),
|
|
95
|
+
);
|
|
96
|
+
expect(onError.mock.calls[0][0].message).toBe("boom");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// -------------------------------------------------------------------------
|
|
100
|
+
// Test 7 — onSettled fires on success
|
|
101
|
+
// -------------------------------------------------------------------------
|
|
102
|
+
it("test 7: onSettled(data, null) fires on success", async () => {
|
|
103
|
+
const onSettled = vi.fn();
|
|
104
|
+
const fn = vi.fn().mockResolvedValue("result");
|
|
105
|
+
renderHook(() => useAsync({ fn, onSettled }));
|
|
106
|
+
|
|
107
|
+
await waitFor(() => expect(onSettled).toHaveBeenCalledWith("result", null));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// -------------------------------------------------------------------------
|
|
111
|
+
// Test 8 — onSettled fires on error
|
|
112
|
+
// -------------------------------------------------------------------------
|
|
113
|
+
it("test 8: onSettled(null, error) fires on error", async () => {
|
|
114
|
+
const onSettled = vi.fn();
|
|
115
|
+
const fn = vi.fn().mockRejectedValue(new Error("err"));
|
|
116
|
+
renderHook(() => useAsync({ fn, onSettled }));
|
|
117
|
+
|
|
118
|
+
await waitFor(() =>
|
|
119
|
+
expect(onSettled).toHaveBeenCalledWith(null, expect.any(Error)),
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// -------------------------------------------------------------------------
|
|
124
|
+
// Test 9 — latest callback ref: change onSuccess between renders
|
|
125
|
+
// -------------------------------------------------------------------------
|
|
126
|
+
it("test 9: callback identity — latest onSuccess called, not captured", async () => {
|
|
127
|
+
const first = vi.fn();
|
|
128
|
+
const second = vi.fn();
|
|
129
|
+
const d = deferred<string>();
|
|
130
|
+
const fn = vi.fn().mockReturnValue(d.promise);
|
|
131
|
+
|
|
132
|
+
const { rerender } = renderHook(
|
|
133
|
+
({ onSuccess }: { onSuccess: (data: string) => void }) =>
|
|
134
|
+
useAsync({ fn, onSuccess }),
|
|
135
|
+
{ initialProps: { onSuccess: first } },
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// Change the callback before the promise resolves
|
|
139
|
+
rerender({ onSuccess: second });
|
|
140
|
+
|
|
141
|
+
await act(async () => {
|
|
142
|
+
d.resolve("data");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
await waitFor(() => expect(second).toHaveBeenCalledWith("data"));
|
|
146
|
+
expect(first).not.toHaveBeenCalled();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// -------------------------------------------------------------------------
|
|
150
|
+
// Test 10 — refetch triggers new execution
|
|
151
|
+
// -------------------------------------------------------------------------
|
|
152
|
+
it("test 10: refetch transitions to pending and calls fn again", async () => {
|
|
153
|
+
const fn = vi.fn().mockResolvedValue("value");
|
|
154
|
+
const { result } = renderHook(() => useAsync({ fn }));
|
|
155
|
+
|
|
156
|
+
await waitFor(() => expect(result.current.status).toBe("success"));
|
|
157
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
158
|
+
|
|
159
|
+
act(() => result.current.refetch());
|
|
160
|
+
|
|
161
|
+
await waitFor(() => expect(fn).toHaveBeenCalledTimes(2));
|
|
162
|
+
await waitFor(() => expect(result.current.status).toBe("success"));
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// -------------------------------------------------------------------------
|
|
166
|
+
// Test 11 — race condition: second call result wins
|
|
167
|
+
// -------------------------------------------------------------------------
|
|
168
|
+
it("test 11: race condition — second call result wins, first discarded", async () => {
|
|
169
|
+
const first = deferred<string>();
|
|
170
|
+
const second = deferred<string>();
|
|
171
|
+
let callCount = 0;
|
|
172
|
+
const fn = vi.fn().mockImplementation((_signal: AbortSignal) => {
|
|
173
|
+
callCount++;
|
|
174
|
+
return callCount === 1 ? first.promise : second.promise;
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const { result } = renderHook(
|
|
178
|
+
({ deps }: { deps: number[] }) => useAsync({ fn, deps }),
|
|
179
|
+
{ initialProps: { deps: [1] } },
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// Wait until fn is called (first call in flight)
|
|
183
|
+
await waitFor(() => expect(fn).toHaveBeenCalledTimes(1));
|
|
184
|
+
|
|
185
|
+
// Trigger second call by changing deps
|
|
186
|
+
act(() => {
|
|
187
|
+
// rerender with new deps to trigger second call
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Instead use refetch for the second call
|
|
191
|
+
act(() => result.current.refetch());
|
|
192
|
+
await waitFor(() => expect(fn).toHaveBeenCalledTimes(2));
|
|
193
|
+
|
|
194
|
+
// Resolve second first, then first — first result should be discarded
|
|
195
|
+
await act(async () => {
|
|
196
|
+
second.resolve("second-result");
|
|
197
|
+
});
|
|
198
|
+
await waitFor(() => expect(result.current.status).toBe("success"));
|
|
199
|
+
expect(result.current.data).toBe("second-result");
|
|
200
|
+
|
|
201
|
+
// Now resolve the stale first call — state should NOT change
|
|
202
|
+
await act(async () => {
|
|
203
|
+
first.resolve("stale-result");
|
|
204
|
+
});
|
|
205
|
+
// State must remain second-result
|
|
206
|
+
expect(result.current.data).toBe("second-result");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// -------------------------------------------------------------------------
|
|
210
|
+
// Test 12 — unmount during pending: no setState after unmount
|
|
211
|
+
// -------------------------------------------------------------------------
|
|
212
|
+
it("test 12: unmount during pending — no setState after unmount", async () => {
|
|
213
|
+
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
214
|
+
const d = deferred<string>();
|
|
215
|
+
const fn = vi.fn().mockReturnValue(d.promise);
|
|
216
|
+
|
|
217
|
+
const { unmount } = renderHook(() => useAsync({ fn }));
|
|
218
|
+
|
|
219
|
+
// Unmount before the promise resolves
|
|
220
|
+
unmount();
|
|
221
|
+
|
|
222
|
+
// Resolve after unmount
|
|
223
|
+
await act(async () => {
|
|
224
|
+
d.resolve("late");
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// No React "can't perform state update on unmounted component" error
|
|
228
|
+
expect(consoleSpy).not.toHaveBeenCalledWith(
|
|
229
|
+
expect.stringContaining("unmounted"),
|
|
230
|
+
);
|
|
231
|
+
consoleSpy.mockRestore();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// -------------------------------------------------------------------------
|
|
235
|
+
// Test 13 — AbortSignal passed to fn
|
|
236
|
+
// -------------------------------------------------------------------------
|
|
237
|
+
it("test 13: AbortSignal instance passed to fn", () => {
|
|
238
|
+
let receivedSignal: unknown;
|
|
239
|
+
const fn = vi.fn().mockImplementation((signal: AbortSignal) => {
|
|
240
|
+
receivedSignal = signal;
|
|
241
|
+
return new Promise(() => {}); // never resolves
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
renderHook(() => useAsync({ fn }));
|
|
245
|
+
|
|
246
|
+
expect(receivedSignal).toBeInstanceOf(AbortSignal);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// -------------------------------------------------------------------------
|
|
250
|
+
// Test 14 — keepPreviousData: true — data preserved during re-fetch
|
|
251
|
+
// -------------------------------------------------------------------------
|
|
252
|
+
it("test 14: keepPreviousData true — previous data preserved while pending", async () => {
|
|
253
|
+
const first = deferred<string>();
|
|
254
|
+
let callCount = 0;
|
|
255
|
+
const fn = vi.fn().mockImplementation(() => {
|
|
256
|
+
callCount++;
|
|
257
|
+
if (callCount === 1) return first.promise;
|
|
258
|
+
return new Promise(() => {}); // second call never resolves
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const { result } = renderHook(() =>
|
|
262
|
+
useAsync({ fn, keepPreviousData: true }),
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
// Resolve first call
|
|
266
|
+
await act(async () => {
|
|
267
|
+
first.resolve("first-data");
|
|
268
|
+
});
|
|
269
|
+
await waitFor(() => expect(result.current.status).toBe("success"));
|
|
270
|
+
expect(result.current.data).toBe("first-data");
|
|
271
|
+
|
|
272
|
+
// Trigger re-fetch
|
|
273
|
+
act(() => result.current.refetch());
|
|
274
|
+
|
|
275
|
+
await waitFor(() => expect(result.current.status).toBe("pending"));
|
|
276
|
+
// Previous data must still be present
|
|
277
|
+
expect(result.current.data).toBe("first-data");
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// -------------------------------------------------------------------------
|
|
281
|
+
// Test 15 — keepPreviousData: false — data nulled on re-fetch start
|
|
282
|
+
// -------------------------------------------------------------------------
|
|
283
|
+
it("test 15: keepPreviousData false — data null immediately on re-fetch", async () => {
|
|
284
|
+
const first = deferred<string>();
|
|
285
|
+
let callCount = 0;
|
|
286
|
+
const fn = vi.fn().mockImplementation(() => {
|
|
287
|
+
callCount++;
|
|
288
|
+
if (callCount === 1) return first.promise;
|
|
289
|
+
return new Promise(() => {}); // second call never resolves
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const { result } = renderHook(() =>
|
|
293
|
+
useAsync({ fn, keepPreviousData: false }),
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
await act(async () => {
|
|
297
|
+
first.resolve("first-data");
|
|
298
|
+
});
|
|
299
|
+
await waitFor(() => expect(result.current.status).toBe("success"));
|
|
300
|
+
expect(result.current.data).toBe("first-data");
|
|
301
|
+
|
|
302
|
+
// Trigger re-fetch
|
|
303
|
+
act(() => result.current.refetch());
|
|
304
|
+
|
|
305
|
+
await waitFor(() => expect(result.current.status).toBe("pending"));
|
|
306
|
+
// Data must be null now
|
|
307
|
+
expect(result.current.data).toBeNull();
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// -------------------------------------------------------------------------
|
|
311
|
+
// Test 16 — dep change triggers re-run
|
|
312
|
+
// -------------------------------------------------------------------------
|
|
313
|
+
it("test 16: dep change triggers abort of previous call and starts new one", async () => {
|
|
314
|
+
const receivedSignals: AbortSignal[] = [];
|
|
315
|
+
const fn = vi.fn().mockImplementation((signal: AbortSignal) => {
|
|
316
|
+
receivedSignals.push(signal);
|
|
317
|
+
return new Promise<string>((resolve) => {
|
|
318
|
+
// Resolve after a tick so there's time for the signal to abort
|
|
319
|
+
setTimeout(() => resolve("data"), 10);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const { rerender } = renderHook(
|
|
324
|
+
({ dep }: { dep: string }) => useAsync({ fn, deps: [dep] }),
|
|
325
|
+
{ initialProps: { dep: "a" } },
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
await waitFor(() => expect(fn).toHaveBeenCalledTimes(1));
|
|
329
|
+
|
|
330
|
+
// Change dep — should trigger second call
|
|
331
|
+
rerender({ dep: "b" });
|
|
332
|
+
|
|
333
|
+
await waitFor(() => expect(fn).toHaveBeenCalledTimes(2));
|
|
334
|
+
|
|
335
|
+
// First signal must be aborted
|
|
336
|
+
expect(receivedSignals[0].aborted).toBe(true);
|
|
337
|
+
// Second signal is still active
|
|
338
|
+
expect(receivedSignals[1].aborted).toBe(false);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// -------------------------------------------------------------------------
|
|
342
|
+
// Test 17 — enabled: false → true transition
|
|
343
|
+
// -------------------------------------------------------------------------
|
|
344
|
+
it("test 17: enabled false → true transitions to pending and calls fn", async () => {
|
|
345
|
+
const fn = vi.fn().mockResolvedValue("ok");
|
|
346
|
+
const { result, rerender } = renderHook(
|
|
347
|
+
({ enabled }: { enabled: boolean }) => useAsync({ fn, enabled }),
|
|
348
|
+
{ initialProps: { enabled: false } },
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
expect(result.current.status).toBe("idle");
|
|
352
|
+
expect(fn).not.toHaveBeenCalled();
|
|
353
|
+
|
|
354
|
+
rerender({ enabled: true });
|
|
355
|
+
|
|
356
|
+
await waitFor(() => expect(fn).toHaveBeenCalledTimes(1));
|
|
357
|
+
await waitFor(() => expect(result.current.status).toBe("success"));
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// -------------------------------------------------------------------------
|
|
361
|
+
// Test 18 — refetch identity stable across re-renders
|
|
362
|
+
// -------------------------------------------------------------------------
|
|
363
|
+
it("test 18: refetch reference is stable across re-renders", async () => {
|
|
364
|
+
const fn = vi.fn().mockResolvedValue("val");
|
|
365
|
+
const { result, rerender } = renderHook(() => useAsync({ fn }));
|
|
366
|
+
|
|
367
|
+
await waitFor(() => expect(result.current.status).toBe("success"));
|
|
368
|
+
|
|
369
|
+
const first = result.current.refetch;
|
|
370
|
+
rerender();
|
|
371
|
+
const second = result.current.refetch;
|
|
372
|
+
|
|
373
|
+
expect(first).toBe(second);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// -------------------------------------------------------------------------
|
|
377
|
+
// Test 19 — SSR snapshot: hook initial return is idle (effect doesn't run)
|
|
378
|
+
// -------------------------------------------------------------------------
|
|
379
|
+
it("test 19: SSR snapshot — initial return shape with enabled false", () => {
|
|
380
|
+
// Simulate SSR: the hook returns idle state on first render
|
|
381
|
+
// when enabled is false (effect-free path)
|
|
382
|
+
const fn = vi.fn();
|
|
383
|
+
const { result } = renderHook(() => useAsync({ fn, enabled: false }));
|
|
384
|
+
|
|
385
|
+
// Immediate synchronous check before any async work
|
|
386
|
+
expect(result.current).toMatchObject({
|
|
387
|
+
status: "idle",
|
|
388
|
+
loading: false,
|
|
389
|
+
data: null,
|
|
390
|
+
error: null,
|
|
391
|
+
});
|
|
392
|
+
expect(typeof result.current.refetch).toBe("function");
|
|
393
|
+
expect(typeof result.current.isIdle).toBe("boolean");
|
|
394
|
+
expect(typeof result.current.isSuccess).toBe("boolean");
|
|
395
|
+
expect(typeof result.current.isError).toBe("boolean");
|
|
396
|
+
});
|
|
397
|
+
});
|
|
@@ -1,57 +1,135 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useLatestRef } from "../internal/use-latest-ref";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
export type AsyncStatus = "idle" | "pending" | "success" | "error";
|
|
5
|
+
|
|
6
|
+
export interface UseAsyncOptions<T> {
|
|
7
|
+
fn: (signal: AbortSignal) => Promise<T>;
|
|
8
|
+
deps?: readonly unknown[];
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
keepPreviousData?: boolean;
|
|
5
11
|
onSuccess?: (data: T) => void;
|
|
6
|
-
|
|
12
|
+
onError?: (error: Error) => void;
|
|
13
|
+
onSettled?: (data: T | null, error: Error | null) => void;
|
|
7
14
|
}
|
|
8
15
|
|
|
9
|
-
interface
|
|
10
|
-
loading: boolean;
|
|
11
|
-
error: Error | null;
|
|
16
|
+
export interface UseAsyncReturn<T> {
|
|
12
17
|
data: T | null;
|
|
18
|
+
error: Error | null;
|
|
19
|
+
status: AsyncStatus;
|
|
20
|
+
loading: boolean;
|
|
21
|
+
isIdle: boolean;
|
|
22
|
+
isSuccess: boolean;
|
|
23
|
+
isError: boolean;
|
|
13
24
|
refetch: () => void;
|
|
14
25
|
}
|
|
15
26
|
|
|
16
|
-
interface Props<T> extends UseAsyncOptions<T> {
|
|
17
|
-
fn: () => Promise<T>;
|
|
18
|
-
dependencies?: DependencyList;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
27
|
export function useAsync<T>({
|
|
22
28
|
fn,
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
deps = [],
|
|
30
|
+
enabled = true,
|
|
31
|
+
keepPreviousData = false,
|
|
25
32
|
onSuccess,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const [
|
|
33
|
+
onError,
|
|
34
|
+
onSettled,
|
|
35
|
+
}: UseAsyncOptions<T>): UseAsyncReturn<T> {
|
|
36
|
+
const [status, setStatus] = useState<AsyncStatus>(
|
|
37
|
+
enabled ? "pending" : "idle",
|
|
38
|
+
);
|
|
30
39
|
const [data, setData] = useState<T | null>(null);
|
|
40
|
+
const [error, setError] = useState<Error | null>(null);
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
// Stable callback refs — never cause effect re-subscriptions
|
|
43
|
+
const fnRef = useLatestRef(fn);
|
|
44
|
+
const onSuccessRef = useLatestRef(onSuccess);
|
|
45
|
+
const onErrorRef = useLatestRef(onError);
|
|
46
|
+
const onSettledRef = useLatestRef(onSettled);
|
|
47
|
+
|
|
48
|
+
// Monotonically incrementing request ID — guards stale setState
|
|
49
|
+
const requestIdRef = useRef(0);
|
|
50
|
+
|
|
51
|
+
// Tick cell — bumping this from refetch() forces the effect to re-run
|
|
52
|
+
// without creating a new execute function identity
|
|
53
|
+
const [refetchTick, setRefetchTick] = useState(0);
|
|
54
|
+
|
|
55
|
+
// Stable execute — never changes identity; receives AbortSignal + request id
|
|
56
|
+
const execute = useCallback(
|
|
57
|
+
async (signal: AbortSignal, id: number): Promise<void> => {
|
|
58
|
+
let settled: T | null = null;
|
|
59
|
+
let settledError: Error | null = null;
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const result = await fnRef.current(signal);
|
|
63
|
+
|
|
64
|
+
// Stale-skip: abort or superseded by a newer request
|
|
65
|
+
if (id !== requestIdRef.current || signal.aborted) return;
|
|
66
|
+
|
|
67
|
+
settled = result;
|
|
39
68
|
setData(result);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
69
|
+
setStatus("success");
|
|
70
|
+
onSuccessRef.current?.(result);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
// Abort errors are swallowed — not surfaced to error state or onError
|
|
73
|
+
if (signal.aborted) return;
|
|
74
|
+
|
|
75
|
+
// Stale-skip for non-abort errors too
|
|
76
|
+
if (id !== requestIdRef.current) return;
|
|
77
|
+
|
|
78
|
+
const coerced = err instanceof Error ? err : new Error(String(err));
|
|
79
|
+
settledError = coerced;
|
|
80
|
+
setError(coerced);
|
|
81
|
+
setStatus("error");
|
|
82
|
+
onErrorRef.current?.(coerced);
|
|
83
|
+
} finally {
|
|
84
|
+
// Only call onSettled if this is still the current request and not aborted
|
|
85
|
+
if (id === requestIdRef.current && !signal.aborted) {
|
|
86
|
+
onSettledRef.current?.(settled, settledError);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
// All deps are refs — this callback is effectively stable
|
|
91
|
+
[fnRef, onSuccessRef, onErrorRef, onSettledRef],
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// Stable refetch — bumps tick to trigger the effect
|
|
95
|
+
const refetch = useCallback(() => {
|
|
96
|
+
setRefetchTick((n) => n + 1);
|
|
97
|
+
}, []);
|
|
50
98
|
|
|
51
99
|
useEffect(() => {
|
|
52
|
-
|
|
53
|
-
|
|
100
|
+
if (!enabled) {
|
|
101
|
+
setStatus("idle");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
54
104
|
|
|
55
|
-
|
|
56
|
-
|
|
105
|
+
// Bump request ID and capture local copy for stale-skip checks
|
|
106
|
+
requestIdRef.current += 1;
|
|
107
|
+
const id = requestIdRef.current;
|
|
108
|
+
|
|
109
|
+
const controller = new AbortController();
|
|
110
|
+
|
|
111
|
+
setStatus("pending");
|
|
112
|
+
setError(null);
|
|
113
|
+
if (!keepPreviousData) {
|
|
114
|
+
setData(null);
|
|
115
|
+
}
|
|
57
116
|
|
|
117
|
+
execute(controller.signal, id);
|
|
118
|
+
|
|
119
|
+
return () => {
|
|
120
|
+
controller.abort();
|
|
121
|
+
};
|
|
122
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
123
|
+
}, [...deps, enabled, execute, refetchTick]);
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
data,
|
|
127
|
+
error,
|
|
128
|
+
status,
|
|
129
|
+
loading: status === "pending",
|
|
130
|
+
isIdle: status === "idle",
|
|
131
|
+
isSuccess: status === "success",
|
|
132
|
+
isError: status === "error",
|
|
133
|
+
refetch,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./use-boolean";
|