stunk 2.8.1 → 3.0.0-beta.1
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/README.md +228 -32
- package/dist/chunk-3DOB632D.js +1 -0
- package/dist/chunk-KWDC37TK.js +1 -0
- package/dist/{core-DMY69lzg.d.cts → core-DsoxfUCH.d.cts} +6 -1
- package/dist/{core-DMY69lzg.d.ts → core-DsoxfUCH.d.ts} +6 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +484 -150
- package/dist/index.d.ts +484 -150
- package/dist/index.js +1 -1
- package/dist/middleware/index.cjs +1 -1
- package/dist/middleware/index.d.cts +77 -23
- package/dist/middleware/index.d.ts +77 -23
- package/dist/middleware/index.js +1 -1
- package/dist/mutation-BGXZyZXA.d.ts +214 -0
- package/dist/mutation-Txd2ni6w.d.cts +214 -0
- package/dist/query/index.cjs +1 -0
- package/dist/query/index.d.cts +107 -0
- package/dist/query/index.d.ts +107 -0
- package/dist/query/index.js +1 -0
- package/dist/use-react/index.cjs +1 -1
- package/dist/use-react/index.d.cts +131 -94
- package/dist/use-react/index.d.ts +131 -94
- package/dist/use-react/index.js +1 -1
- package/package.json +122 -121
|
@@ -1,98 +1,52 @@
|
|
|
1
|
-
import { C as Chunk } from '../core-
|
|
1
|
+
import { C as Chunk } from '../core-DsoxfUCH.cjs';
|
|
2
|
+
import { P as PaginatedAsyncChunk, a as PaginationState, A as AsyncChunk, I as InfiniteAsyncChunk, M as Mutation, b as MutationResult } from '../mutation-Txd2ni6w.cjs';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
5
|
+
* Subscribes to a chunk and returns its current value along with `set`, `reset`, and `destroy`.
|
|
6
|
+
*
|
|
7
|
+
* Pass an optional `selector` to derive a slice of the value and avoid
|
|
8
|
+
* unnecessary re-renders when unrelated fields change.
|
|
9
|
+
*
|
|
10
|
+
* @param chunk - The chunk to subscribe to.
|
|
11
|
+
* @param selector - Optional function to select a derived value.
|
|
12
|
+
* @returns `[value, set, reset, destroy]`
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const [count, setCount, reset] = useChunk(countChunk);
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Only re-renders when `name` changes
|
|
19
|
+
* const [name, setUser] = useChunk(userChunk, u => u.name);
|
|
6
20
|
*/
|
|
7
21
|
declare function useChunk<T, S = T>(chunk: Chunk<T>, selector?: (value: T) => S): readonly [S, (valueOrUpdater: T | ((currentValue: T) => T)) => void, () => void, () => void];
|
|
8
22
|
|
|
9
23
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* A hook that computes a value based on multiple chunks.
|
|
22
|
-
* Automatically re-computes when any dependency changes.
|
|
23
|
-
*/
|
|
24
|
-
declare function useComputed<TDeps extends Chunk<any>[], TResult>(dependencies: [...TDeps], computeFn: (...args: DependencyValues<TDeps>) => TResult): TResult;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* A lightweight hook that subscribes to a chunk and returns only its current value.
|
|
28
|
-
* Useful for read-only components that don't need to update the chunk.
|
|
24
|
+
* Subscribes to a chunk and returns only its current value.
|
|
25
|
+
* Use this in read-only components that never need to call `set`.
|
|
26
|
+
*
|
|
27
|
+
* @param chunk - The chunk to subscribe to.
|
|
28
|
+
* @param selector - Optional function to select a derived value.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const name = useChunkValue(userChunk, u => u.name);
|
|
29
32
|
*/
|
|
30
33
|
declare function useChunkValue<T, S = T>(chunk: Chunk<T>, selector?: (value: T) => S): S;
|
|
31
34
|
|
|
32
|
-
/**
|
|
33
|
-
* A hook that subscribes to a specific property of a chunk.
|
|
34
|
-
* This optimizes renders by only updating when the selected property changes.
|
|
35
|
-
*/
|
|
36
|
-
declare function useChunkProperty<T, K extends keyof T>(chunk: Chunk<T>, property: K): T[K];
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Hook to read values from multiple chunks at once.
|
|
40
|
-
* Only re-renders when any of the chunk values change.
|
|
41
|
-
*/
|
|
42
|
-
declare function useChunkValues<T extends Chunk<any>[]>(chunks: [...T]): {
|
|
43
|
-
[K in keyof T]: T[K] extends Chunk<infer U> ? U : never;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
interface AsyncState<T, E extends Error> {
|
|
47
|
-
loading: boolean;
|
|
48
|
-
error: E | null;
|
|
49
|
-
data: T | null;
|
|
50
|
-
lastFetched?: number;
|
|
51
|
-
}
|
|
52
|
-
interface PaginationState {
|
|
53
|
-
page: number;
|
|
54
|
-
pageSize: number;
|
|
55
|
-
total?: number;
|
|
56
|
-
hasMore?: boolean;
|
|
57
|
-
}
|
|
58
|
-
interface AsyncStateWithPagination<T, E extends Error> extends AsyncState<T, E> {
|
|
59
|
-
pagination?: PaginationState;
|
|
60
|
-
}
|
|
61
|
-
interface AsyncChunk<T, E extends Error = Error> extends Chunk<AsyncStateWithPagination<T, E>> {
|
|
62
|
-
/** Force reload data */
|
|
63
|
-
reload: (params?: any) => Promise<void>;
|
|
64
|
-
/** Smart refresh - respects stale time */
|
|
65
|
-
refresh: (params?: any) => Promise<void>;
|
|
66
|
-
/** Mutate data directly */
|
|
67
|
-
mutate: (mutator: (currentData: T | null) => T) => void;
|
|
68
|
-
/** Reset to initial state */
|
|
69
|
-
reset: () => void;
|
|
70
|
-
/** Clean up intervals */
|
|
71
|
-
cleanup: () => void;
|
|
72
|
-
}
|
|
73
|
-
interface PaginatedAsyncChunk<T, E extends Error = Error> extends AsyncChunk<T, E> {
|
|
74
|
-
/** Load next page */
|
|
75
|
-
nextPage: () => Promise<void>;
|
|
76
|
-
/** Load previous page */
|
|
77
|
-
prevPage: () => Promise<void>;
|
|
78
|
-
/** Go to specific page */
|
|
79
|
-
goToPage: (page: number) => Promise<void>;
|
|
80
|
-
/** Reset pagination to first page */
|
|
81
|
-
resetPagination: () => Promise<void>;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
35
|
interface UseAsyncChunkResult<T, E extends Error, P extends Record<string, any>> {
|
|
85
36
|
data: T | null;
|
|
86
37
|
loading: boolean;
|
|
87
38
|
error: E | null;
|
|
88
39
|
lastFetched?: number;
|
|
40
|
+
/** True when showing stale data while a new fetch is in progress (keepPreviousData: true) */
|
|
41
|
+
isPlaceholderData: boolean;
|
|
89
42
|
reload: (params?: Partial<P>) => Promise<void>;
|
|
90
43
|
refresh: (params?: Partial<P>) => Promise<void>;
|
|
91
44
|
mutate: (mutator: (currentData: T | null) => T) => void;
|
|
92
45
|
reset: () => void;
|
|
93
46
|
}
|
|
94
47
|
interface UseAsyncChunkResultWithParams<T, E extends Error, P extends Record<string, any>> extends UseAsyncChunkResult<T, E, P> {
|
|
95
|
-
setParams: (params: Partial<P
|
|
48
|
+
setParams: (params: Partial<Record<keyof P, P[keyof P] | null>>) => void;
|
|
49
|
+
clearParams: () => void;
|
|
96
50
|
}
|
|
97
51
|
interface UseAsyncChunkResultWithPagination<T, E extends Error, P extends Record<string, any>> extends UseAsyncChunkResult<T, E, P> {
|
|
98
52
|
pagination?: PaginationState;
|
|
@@ -103,39 +57,122 @@ interface UseAsyncChunkResultWithPagination<T, E extends Error, P extends Record
|
|
|
103
57
|
}
|
|
104
58
|
interface UseAsyncChunkResultWithParamsAndPagination<T, E extends Error, P extends Record<string, any>> extends UseAsyncChunkResultWithParams<T, E, P>, Omit<UseAsyncChunkResultWithPagination<T, E, P>, keyof UseAsyncChunkResult<T, E, P>> {
|
|
105
59
|
}
|
|
106
|
-
/**
|
|
107
|
-
* A hook that handles asynchronous state with built-in reactivity.
|
|
108
|
-
* Provides loading, error, and data states with full asyncChunk functionality.
|
|
109
|
-
*/
|
|
110
60
|
interface UseAsyncChunkOptions<P extends Record<string, any> = {}> {
|
|
111
|
-
/** Initial parameters to pass to the fetcher */
|
|
61
|
+
/** Initial parameters to pass to the fetcher on mount */
|
|
112
62
|
initialParams?: Partial<P>;
|
|
113
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Force a fetch on mount even when the chunk has no params.
|
|
65
|
+
* Ignored if initialParams is provided.
|
|
66
|
+
* (default: false)
|
|
67
|
+
*/
|
|
114
68
|
fetchOnMount?: boolean;
|
|
115
69
|
}
|
|
116
70
|
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: PaginatedAsyncChunk<T, E> & {
|
|
117
71
|
setParams: (params: Partial<P>) => void;
|
|
118
|
-
}, options?: UseAsyncChunkOptions<P>
|
|
119
|
-
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: PaginatedAsyncChunk<T, E>, options?: UseAsyncChunkOptions<P>
|
|
72
|
+
}, options?: UseAsyncChunkOptions<P>): UseAsyncChunkResultWithParamsAndPagination<T, E, P>;
|
|
73
|
+
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: PaginatedAsyncChunk<T, E>, options?: UseAsyncChunkOptions<P>): UseAsyncChunkResultWithPagination<T, E, P>;
|
|
120
74
|
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: AsyncChunk<T, E> & {
|
|
121
75
|
setParams: (params: Partial<P>) => void;
|
|
122
|
-
}, options?: UseAsyncChunkOptions<P>
|
|
123
|
-
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: AsyncChunk<T, E>, options?: UseAsyncChunkOptions<P>
|
|
76
|
+
}, options?: UseAsyncChunkOptions<P>): UseAsyncChunkResultWithParams<T, E, P>;
|
|
77
|
+
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: AsyncChunk<T, E>, options?: UseAsyncChunkOptions<P>): UseAsyncChunkResult<T, E, P>;
|
|
124
78
|
|
|
125
79
|
interface UseInfiniteAsyncChunkOptions<P extends Record<string, any>> extends Omit<UseAsyncChunkOptions<P>, 'initialParams'> {
|
|
126
|
-
/** Initial parameters
|
|
80
|
+
/** Initial parameters — page and pageSize are managed automatically */
|
|
127
81
|
initialParams?: Omit<Partial<P>, 'page' | 'pageSize'>;
|
|
128
|
-
/**
|
|
82
|
+
/** Automatically load next page when sentinel enters viewport (default: true) */
|
|
129
83
|
autoLoad?: boolean;
|
|
130
|
-
/**
|
|
84
|
+
/** IntersectionObserver threshold — 0.0 to 1.0 (default: 1.0) */
|
|
131
85
|
threshold?: number;
|
|
132
86
|
}
|
|
87
|
+
interface UseInfiniteAsyncChunkResult<T, E extends Error, P extends Record<string, any>> {
|
|
88
|
+
data: T[] | null;
|
|
89
|
+
loading: boolean;
|
|
90
|
+
error: E | null;
|
|
91
|
+
lastFetched?: number;
|
|
92
|
+
isPlaceholderData: boolean;
|
|
93
|
+
/** True when fetching a new page while existing data is already loaded */
|
|
94
|
+
isFetchingMore: boolean;
|
|
95
|
+
/** True if more pages are available */
|
|
96
|
+
hasMore: boolean;
|
|
97
|
+
reload: (params?: Partial<P>) => Promise<void>;
|
|
98
|
+
refresh: (params?: Partial<P>) => Promise<void>;
|
|
99
|
+
mutate: (mutator: (currentData: T[] | null) => T[]) => void;
|
|
100
|
+
reset: () => void;
|
|
101
|
+
nextPage: () => Promise<void>;
|
|
102
|
+
prevPage: () => Promise<void>;
|
|
103
|
+
goToPage: (page: number) => Promise<void>;
|
|
104
|
+
resetPagination: () => Promise<void>;
|
|
105
|
+
/** Manually trigger loading the next page */
|
|
106
|
+
loadMore: () => void;
|
|
107
|
+
/** Attach this ref to a sentinel element at the bottom of your list */
|
|
108
|
+
observerTarget: React.RefObject<HTMLElement>;
|
|
109
|
+
}
|
|
133
110
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
111
|
+
* Subscribes to an infinite async chunk and wires up automatic infinite scroll.
|
|
112
|
+
*
|
|
113
|
+
* Attach `observerTarget` to a sentinel element at the bottom of your list —
|
|
114
|
+
* the next page loads automatically when it enters the viewport.
|
|
115
|
+
* Use `loadMore()` for manual triggering.
|
|
116
|
+
*
|
|
117
|
+
* @param chunk - An `InfiniteAsyncChunk` instance.
|
|
118
|
+
* @param options.autoLoad - Auto-load on scroll (default: true).
|
|
119
|
+
* @param options.threshold - IntersectionObserver threshold 0.0–1.0 (default: 1.0).
|
|
120
|
+
* @param options.initialParams - Initial params excluding `page` and `pageSize`.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* const { data, loading, hasMore, observerTarget, loadMore } = useInfiniteAsyncChunk(postsChunk);
|
|
124
|
+
*
|
|
125
|
+
* return (
|
|
126
|
+
* <>
|
|
127
|
+
* {data?.map(post => <Post key={post.id} {...post} />)}
|
|
128
|
+
* <div ref={observerTarget} />
|
|
129
|
+
* </>
|
|
130
|
+
* );
|
|
136
131
|
*/
|
|
137
|
-
declare function useInfiniteAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(
|
|
138
|
-
|
|
139
|
-
|
|
132
|
+
declare function useInfiniteAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(chunk: InfiniteAsyncChunk<T, E, P>, options?: UseInfiniteAsyncChunkOptions<P>): UseInfiniteAsyncChunkResult<T, E, P>;
|
|
133
|
+
|
|
134
|
+
interface UseMutationResult<TData, TError extends Error = Error, TVariables = void> {
|
|
135
|
+
/** Current mutation state */
|
|
136
|
+
loading: boolean;
|
|
137
|
+
data: TData | null;
|
|
138
|
+
error: TError | null;
|
|
139
|
+
isSuccess: boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Execute the mutation. Always resolves — never throws.
|
|
142
|
+
* Safe to fire and forget, or await for local UI control.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* // Fire and forget
|
|
146
|
+
* mutate({ title: 'Hello' });
|
|
147
|
+
*
|
|
148
|
+
* // Await for local control — no try/catch needed
|
|
149
|
+
* const { data, error } = await mutate({ title: 'Hello' });
|
|
150
|
+
* if (!error) router.push('/posts');
|
|
151
|
+
*/
|
|
152
|
+
mutate: TVariables extends void ? () => Promise<MutationResult<TData, TError>> : (variables: TVariables) => Promise<MutationResult<TData, TError>>;
|
|
153
|
+
/** Reset mutation state back to initial */
|
|
154
|
+
reset: () => void;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Subscribes to a mutation instance and returns its reactive state with `mutate` and `reset`.
|
|
158
|
+
*
|
|
159
|
+
* @param mutation - A `mutation()` instance from `stunk/query`.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* const createPost = mutation(
|
|
163
|
+
* async (data: NewPost) => fetchAPI('/posts', { method: 'POST', body: data }),
|
|
164
|
+
* { invalidates: [postsChunk] }
|
|
165
|
+
* );
|
|
166
|
+
*
|
|
167
|
+
* function CreatePostForm() {
|
|
168
|
+
* const { mutate, loading, error, isSuccess } = useMutation(createPost);
|
|
169
|
+
*
|
|
170
|
+
* const handleSubmit = async (data: NewPost) => {
|
|
171
|
+
* const { error } = await mutate(data);
|
|
172
|
+
* if (!error) router.push('/posts');
|
|
173
|
+
* };
|
|
174
|
+
* }
|
|
175
|
+
*/
|
|
176
|
+
declare function useMutation<TData, TError extends Error = Error, TVariables = void>(mutation: Mutation<TData, TError, TVariables>): UseMutationResult<TData, TError, TVariables>;
|
|
140
177
|
|
|
141
|
-
export { useAsyncChunk, useChunk,
|
|
178
|
+
export { type UseMutationResult, useAsyncChunk, useChunk, useChunkValue, useInfiniteAsyncChunk, useMutation };
|
|
@@ -1,98 +1,52 @@
|
|
|
1
|
-
import { C as Chunk } from '../core-
|
|
1
|
+
import { C as Chunk } from '../core-DsoxfUCH.js';
|
|
2
|
+
import { P as PaginatedAsyncChunk, a as PaginationState, A as AsyncChunk, I as InfiniteAsyncChunk, M as Mutation, b as MutationResult } from '../mutation-BGXZyZXA.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
5
|
+
* Subscribes to a chunk and returns its current value along with `set`, `reset`, and `destroy`.
|
|
6
|
+
*
|
|
7
|
+
* Pass an optional `selector` to derive a slice of the value and avoid
|
|
8
|
+
* unnecessary re-renders when unrelated fields change.
|
|
9
|
+
*
|
|
10
|
+
* @param chunk - The chunk to subscribe to.
|
|
11
|
+
* @param selector - Optional function to select a derived value.
|
|
12
|
+
* @returns `[value, set, reset, destroy]`
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const [count, setCount, reset] = useChunk(countChunk);
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Only re-renders when `name` changes
|
|
19
|
+
* const [name, setUser] = useChunk(userChunk, u => u.name);
|
|
6
20
|
*/
|
|
7
21
|
declare function useChunk<T, S = T>(chunk: Chunk<T>, selector?: (value: T) => S): readonly [S, (valueOrUpdater: T | ((currentValue: T) => T)) => void, () => void, () => void];
|
|
8
22
|
|
|
9
23
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* A hook that computes a value based on multiple chunks.
|
|
22
|
-
* Automatically re-computes when any dependency changes.
|
|
23
|
-
*/
|
|
24
|
-
declare function useComputed<TDeps extends Chunk<any>[], TResult>(dependencies: [...TDeps], computeFn: (...args: DependencyValues<TDeps>) => TResult): TResult;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* A lightweight hook that subscribes to a chunk and returns only its current value.
|
|
28
|
-
* Useful for read-only components that don't need to update the chunk.
|
|
24
|
+
* Subscribes to a chunk and returns only its current value.
|
|
25
|
+
* Use this in read-only components that never need to call `set`.
|
|
26
|
+
*
|
|
27
|
+
* @param chunk - The chunk to subscribe to.
|
|
28
|
+
* @param selector - Optional function to select a derived value.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const name = useChunkValue(userChunk, u => u.name);
|
|
29
32
|
*/
|
|
30
33
|
declare function useChunkValue<T, S = T>(chunk: Chunk<T>, selector?: (value: T) => S): S;
|
|
31
34
|
|
|
32
|
-
/**
|
|
33
|
-
* A hook that subscribes to a specific property of a chunk.
|
|
34
|
-
* This optimizes renders by only updating when the selected property changes.
|
|
35
|
-
*/
|
|
36
|
-
declare function useChunkProperty<T, K extends keyof T>(chunk: Chunk<T>, property: K): T[K];
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Hook to read values from multiple chunks at once.
|
|
40
|
-
* Only re-renders when any of the chunk values change.
|
|
41
|
-
*/
|
|
42
|
-
declare function useChunkValues<T extends Chunk<any>[]>(chunks: [...T]): {
|
|
43
|
-
[K in keyof T]: T[K] extends Chunk<infer U> ? U : never;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
interface AsyncState<T, E extends Error> {
|
|
47
|
-
loading: boolean;
|
|
48
|
-
error: E | null;
|
|
49
|
-
data: T | null;
|
|
50
|
-
lastFetched?: number;
|
|
51
|
-
}
|
|
52
|
-
interface PaginationState {
|
|
53
|
-
page: number;
|
|
54
|
-
pageSize: number;
|
|
55
|
-
total?: number;
|
|
56
|
-
hasMore?: boolean;
|
|
57
|
-
}
|
|
58
|
-
interface AsyncStateWithPagination<T, E extends Error> extends AsyncState<T, E> {
|
|
59
|
-
pagination?: PaginationState;
|
|
60
|
-
}
|
|
61
|
-
interface AsyncChunk<T, E extends Error = Error> extends Chunk<AsyncStateWithPagination<T, E>> {
|
|
62
|
-
/** Force reload data */
|
|
63
|
-
reload: (params?: any) => Promise<void>;
|
|
64
|
-
/** Smart refresh - respects stale time */
|
|
65
|
-
refresh: (params?: any) => Promise<void>;
|
|
66
|
-
/** Mutate data directly */
|
|
67
|
-
mutate: (mutator: (currentData: T | null) => T) => void;
|
|
68
|
-
/** Reset to initial state */
|
|
69
|
-
reset: () => void;
|
|
70
|
-
/** Clean up intervals */
|
|
71
|
-
cleanup: () => void;
|
|
72
|
-
}
|
|
73
|
-
interface PaginatedAsyncChunk<T, E extends Error = Error> extends AsyncChunk<T, E> {
|
|
74
|
-
/** Load next page */
|
|
75
|
-
nextPage: () => Promise<void>;
|
|
76
|
-
/** Load previous page */
|
|
77
|
-
prevPage: () => Promise<void>;
|
|
78
|
-
/** Go to specific page */
|
|
79
|
-
goToPage: (page: number) => Promise<void>;
|
|
80
|
-
/** Reset pagination to first page */
|
|
81
|
-
resetPagination: () => Promise<void>;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
35
|
interface UseAsyncChunkResult<T, E extends Error, P extends Record<string, any>> {
|
|
85
36
|
data: T | null;
|
|
86
37
|
loading: boolean;
|
|
87
38
|
error: E | null;
|
|
88
39
|
lastFetched?: number;
|
|
40
|
+
/** True when showing stale data while a new fetch is in progress (keepPreviousData: true) */
|
|
41
|
+
isPlaceholderData: boolean;
|
|
89
42
|
reload: (params?: Partial<P>) => Promise<void>;
|
|
90
43
|
refresh: (params?: Partial<P>) => Promise<void>;
|
|
91
44
|
mutate: (mutator: (currentData: T | null) => T) => void;
|
|
92
45
|
reset: () => void;
|
|
93
46
|
}
|
|
94
47
|
interface UseAsyncChunkResultWithParams<T, E extends Error, P extends Record<string, any>> extends UseAsyncChunkResult<T, E, P> {
|
|
95
|
-
setParams: (params: Partial<P
|
|
48
|
+
setParams: (params: Partial<Record<keyof P, P[keyof P] | null>>) => void;
|
|
49
|
+
clearParams: () => void;
|
|
96
50
|
}
|
|
97
51
|
interface UseAsyncChunkResultWithPagination<T, E extends Error, P extends Record<string, any>> extends UseAsyncChunkResult<T, E, P> {
|
|
98
52
|
pagination?: PaginationState;
|
|
@@ -103,39 +57,122 @@ interface UseAsyncChunkResultWithPagination<T, E extends Error, P extends Record
|
|
|
103
57
|
}
|
|
104
58
|
interface UseAsyncChunkResultWithParamsAndPagination<T, E extends Error, P extends Record<string, any>> extends UseAsyncChunkResultWithParams<T, E, P>, Omit<UseAsyncChunkResultWithPagination<T, E, P>, keyof UseAsyncChunkResult<T, E, P>> {
|
|
105
59
|
}
|
|
106
|
-
/**
|
|
107
|
-
* A hook that handles asynchronous state with built-in reactivity.
|
|
108
|
-
* Provides loading, error, and data states with full asyncChunk functionality.
|
|
109
|
-
*/
|
|
110
60
|
interface UseAsyncChunkOptions<P extends Record<string, any> = {}> {
|
|
111
|
-
/** Initial parameters to pass to the fetcher */
|
|
61
|
+
/** Initial parameters to pass to the fetcher on mount */
|
|
112
62
|
initialParams?: Partial<P>;
|
|
113
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Force a fetch on mount even when the chunk has no params.
|
|
65
|
+
* Ignored if initialParams is provided.
|
|
66
|
+
* (default: false)
|
|
67
|
+
*/
|
|
114
68
|
fetchOnMount?: boolean;
|
|
115
69
|
}
|
|
116
70
|
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: PaginatedAsyncChunk<T, E> & {
|
|
117
71
|
setParams: (params: Partial<P>) => void;
|
|
118
|
-
}, options?: UseAsyncChunkOptions<P>
|
|
119
|
-
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: PaginatedAsyncChunk<T, E>, options?: UseAsyncChunkOptions<P>
|
|
72
|
+
}, options?: UseAsyncChunkOptions<P>): UseAsyncChunkResultWithParamsAndPagination<T, E, P>;
|
|
73
|
+
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: PaginatedAsyncChunk<T, E>, options?: UseAsyncChunkOptions<P>): UseAsyncChunkResultWithPagination<T, E, P>;
|
|
120
74
|
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: AsyncChunk<T, E> & {
|
|
121
75
|
setParams: (params: Partial<P>) => void;
|
|
122
|
-
}, options?: UseAsyncChunkOptions<P>
|
|
123
|
-
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: AsyncChunk<T, E>, options?: UseAsyncChunkOptions<P>
|
|
76
|
+
}, options?: UseAsyncChunkOptions<P>): UseAsyncChunkResultWithParams<T, E, P>;
|
|
77
|
+
declare function useAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(asyncChunk: AsyncChunk<T, E>, options?: UseAsyncChunkOptions<P>): UseAsyncChunkResult<T, E, P>;
|
|
124
78
|
|
|
125
79
|
interface UseInfiniteAsyncChunkOptions<P extends Record<string, any>> extends Omit<UseAsyncChunkOptions<P>, 'initialParams'> {
|
|
126
|
-
/** Initial parameters
|
|
80
|
+
/** Initial parameters — page and pageSize are managed automatically */
|
|
127
81
|
initialParams?: Omit<Partial<P>, 'page' | 'pageSize'>;
|
|
128
|
-
/**
|
|
82
|
+
/** Automatically load next page when sentinel enters viewport (default: true) */
|
|
129
83
|
autoLoad?: boolean;
|
|
130
|
-
/**
|
|
84
|
+
/** IntersectionObserver threshold — 0.0 to 1.0 (default: 1.0) */
|
|
131
85
|
threshold?: number;
|
|
132
86
|
}
|
|
87
|
+
interface UseInfiniteAsyncChunkResult<T, E extends Error, P extends Record<string, any>> {
|
|
88
|
+
data: T[] | null;
|
|
89
|
+
loading: boolean;
|
|
90
|
+
error: E | null;
|
|
91
|
+
lastFetched?: number;
|
|
92
|
+
isPlaceholderData: boolean;
|
|
93
|
+
/** True when fetching a new page while existing data is already loaded */
|
|
94
|
+
isFetchingMore: boolean;
|
|
95
|
+
/** True if more pages are available */
|
|
96
|
+
hasMore: boolean;
|
|
97
|
+
reload: (params?: Partial<P>) => Promise<void>;
|
|
98
|
+
refresh: (params?: Partial<P>) => Promise<void>;
|
|
99
|
+
mutate: (mutator: (currentData: T[] | null) => T[]) => void;
|
|
100
|
+
reset: () => void;
|
|
101
|
+
nextPage: () => Promise<void>;
|
|
102
|
+
prevPage: () => Promise<void>;
|
|
103
|
+
goToPage: (page: number) => Promise<void>;
|
|
104
|
+
resetPagination: () => Promise<void>;
|
|
105
|
+
/** Manually trigger loading the next page */
|
|
106
|
+
loadMore: () => void;
|
|
107
|
+
/** Attach this ref to a sentinel element at the bottom of your list */
|
|
108
|
+
observerTarget: React.RefObject<HTMLElement>;
|
|
109
|
+
}
|
|
133
110
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
111
|
+
* Subscribes to an infinite async chunk and wires up automatic infinite scroll.
|
|
112
|
+
*
|
|
113
|
+
* Attach `observerTarget` to a sentinel element at the bottom of your list —
|
|
114
|
+
* the next page loads automatically when it enters the viewport.
|
|
115
|
+
* Use `loadMore()` for manual triggering.
|
|
116
|
+
*
|
|
117
|
+
* @param chunk - An `InfiniteAsyncChunk` instance.
|
|
118
|
+
* @param options.autoLoad - Auto-load on scroll (default: true).
|
|
119
|
+
* @param options.threshold - IntersectionObserver threshold 0.0–1.0 (default: 1.0).
|
|
120
|
+
* @param options.initialParams - Initial params excluding `page` and `pageSize`.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* const { data, loading, hasMore, observerTarget, loadMore } = useInfiniteAsyncChunk(postsChunk);
|
|
124
|
+
*
|
|
125
|
+
* return (
|
|
126
|
+
* <>
|
|
127
|
+
* {data?.map(post => <Post key={post.id} {...post} />)}
|
|
128
|
+
* <div ref={observerTarget} />
|
|
129
|
+
* </>
|
|
130
|
+
* );
|
|
136
131
|
*/
|
|
137
|
-
declare function useInfiniteAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(
|
|
138
|
-
|
|
139
|
-
|
|
132
|
+
declare function useInfiniteAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(chunk: InfiniteAsyncChunk<T, E, P>, options?: UseInfiniteAsyncChunkOptions<P>): UseInfiniteAsyncChunkResult<T, E, P>;
|
|
133
|
+
|
|
134
|
+
interface UseMutationResult<TData, TError extends Error = Error, TVariables = void> {
|
|
135
|
+
/** Current mutation state */
|
|
136
|
+
loading: boolean;
|
|
137
|
+
data: TData | null;
|
|
138
|
+
error: TError | null;
|
|
139
|
+
isSuccess: boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Execute the mutation. Always resolves — never throws.
|
|
142
|
+
* Safe to fire and forget, or await for local UI control.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* // Fire and forget
|
|
146
|
+
* mutate({ title: 'Hello' });
|
|
147
|
+
*
|
|
148
|
+
* // Await for local control — no try/catch needed
|
|
149
|
+
* const { data, error } = await mutate({ title: 'Hello' });
|
|
150
|
+
* if (!error) router.push('/posts');
|
|
151
|
+
*/
|
|
152
|
+
mutate: TVariables extends void ? () => Promise<MutationResult<TData, TError>> : (variables: TVariables) => Promise<MutationResult<TData, TError>>;
|
|
153
|
+
/** Reset mutation state back to initial */
|
|
154
|
+
reset: () => void;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Subscribes to a mutation instance and returns its reactive state with `mutate` and `reset`.
|
|
158
|
+
*
|
|
159
|
+
* @param mutation - A `mutation()` instance from `stunk/query`.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* const createPost = mutation(
|
|
163
|
+
* async (data: NewPost) => fetchAPI('/posts', { method: 'POST', body: data }),
|
|
164
|
+
* { invalidates: [postsChunk] }
|
|
165
|
+
* );
|
|
166
|
+
*
|
|
167
|
+
* function CreatePostForm() {
|
|
168
|
+
* const { mutate, loading, error, isSuccess } = useMutation(createPost);
|
|
169
|
+
*
|
|
170
|
+
* const handleSubmit = async (data: NewPost) => {
|
|
171
|
+
* const { error } = await mutate(data);
|
|
172
|
+
* if (!error) router.push('/posts');
|
|
173
|
+
* };
|
|
174
|
+
* }
|
|
175
|
+
*/
|
|
176
|
+
declare function useMutation<TData, TError extends Error = Error, TVariables = void>(mutation: Mutation<TData, TError, TVariables>): UseMutationResult<TData, TError, TVariables>;
|
|
140
177
|
|
|
141
|
-
export { useAsyncChunk, useChunk,
|
|
178
|
+
export { type UseMutationResult, useAsyncChunk, useChunk, useChunkValue, useInfiniteAsyncChunk, useMutation };
|
package/dist/use-react/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {a}from'../chunk-KWDC37TK.js';import {b}from'../chunk-3DOB632D.js';import {useState,useEffect,useCallback,useRef}from'react';function k(e,s,t={}){let{useShallowEqual:i=false}=t,u=e.get(),n=s(u),a$1=a(n),P=()=>{let m=e.get(),l=s(m);(i?!b(l,n):l!==n)&&(n=l,a$1.set(l));},o=e.subscribe(P),{set:d,reset:T,...E}=a$1;return {...E,derive:m=>k(a$1,m,t),destroy:()=>{o(),a$1.destroy();}}}function U(e,s){let t=s?k(e,s):e,[i,u]=useState(()=>t.get());useEffect(()=>{let o=t.subscribe(d=>{u(()=>d);});return ()=>o()},[t]);let n=useCallback(o=>{e.set(o);},[e]),a=useCallback(()=>{e.reset();},[e]),P=useCallback(()=>{e.destroy();},[e]);return [i,n,a,P]}function _(e,s){let[t]=U(e,s);return t}function x(e){return "nextPage"in e}function M(e){return "setParams"in e}function G(e){return "clearParams"in e}function O(e,s={}){let{initialParams:t,fetchOnMount:i=false}=s,[u,n]=useState(()=>e.get()),a=useRef({initialParams:t,fetchOnMount:i});a.current={initialParams:t,fetchOnMount:i},useEffect(()=>{n(e.get());let r=e.subscribe(q=>{n(q);}),{initialParams:D,fetchOnMount:F}=a.current;return D&&M(e)?e.setParams(D):F&&e.reload(),()=>{r(),e.cleanup();}},[e]);let P=useCallback(r=>e.reload(r),[e]),o=useCallback(r=>e.refresh(r),[e]),d=useCallback(r=>e.mutate(r),[e]),T=useCallback(()=>e.reset(),[e]),E=useCallback(r=>{M(e)&&e.setParams(r);},[e]),m=useCallback(()=>{G(e)&&e.clearParams();},[e]),l=useCallback(()=>x(e)?e.nextPage():Promise.resolve(),[e]),f=useCallback(()=>x(e)?e.prevPage():Promise.resolve(),[e]),h=useCallback(r=>x(e)?e.goToPage(r):Promise.resolve(),[e]),y=useCallback(()=>x(e)?e.resetPagination():Promise.resolve(),[e]),{data:b,loading:v,error:p,lastFetched:g,isPlaceholderData:C=false,pagination:w}=u,A={data:b,loading:v,error:p,lastFetched:g,isPlaceholderData:C,reload:P,refresh:o,mutate:d,reset:T};if(M(e)&&(A.setParams=E,A.clearParams=m),x(e)){let r=A;r.pagination=w,r.nextPage=l,r.prevPage=f,r.goToPage=h,r.resetPagination=y;}return A}function N(e,s={}){let{initialParams:t,autoLoad:i=true,threshold:u=1,fetchOnMount:n}=s,a=O(e,{...t&&{initialParams:t},fetchOnMount:n}),{loading:P,pagination:o,nextPage:d,data:T,error:E,isPlaceholderData:m}=a,l=useRef(P),f=useRef(o?.hasMore??false),h=useRef(d);l.current=P,f.current=o?.hasMore??false,h.current=d;let y=useRef(null);useEffect(()=>{if(!i||typeof window>"u"||!("IntersectionObserver"in window))return;let p=new IntersectionObserver(C=>{C[0].isIntersecting&&!l.current&&f.current&&h.current();},{threshold:u}),g=y.current;return g&&p.observe(g),()=>{g&&p.unobserve(g),p.disconnect();}},[i,u]);let b=useCallback(()=>{!l.current&&f.current&&h.current();},[]),v=P&&T!==null&&T.length>0&&(o?.page??1)>1;return {...a,data:T,error:E,isPlaceholderData:m??false,isFetchingMore:v,hasMore:o?.hasMore??false,loadMore:b,observerTarget:y}}function Y(e){let[s,t]=useState(()=>e.get());useEffect(()=>(t(e.get()),e.subscribe(a=>{t(a);})),[e]);let i=useCallback((...n)=>e.mutate(...n),[e]),u=useCallback(()=>e.reset(),[e]);return {loading:s.loading,data:s.data,error:s.error,isSuccess:s.isSuccess,mutate:i,reset:u}}export{O as useAsyncChunk,U as useChunk,_ as useChunkValue,N as useInfiniteAsyncChunk,Y as useMutation};
|