floppy-disk 1.4.1-beta.1 → 2.0.0-alpha.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.
@@ -1,4 +1,4 @@
1
- import { CreateStoresOptions, StoreKey } from './create-stores';
1
+ import { CreateStoresOptions, StoreKey, UseStores } from './create-stores';
2
2
  export type QueryStatus = 'loading' | 'success' | 'error';
3
3
  export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = {
4
4
  /**
@@ -22,29 +22,10 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
22
22
  */
23
23
  fetchNextPage: () => void;
24
24
  markAsStale: () => void;
25
- reset: () => void;
26
25
  /**
27
- * Set query response & data.
28
- */
29
- setResponse: (responseSetter: TResponse | ((state: QueryState<TKey, TResponse, TData, TError>) => TResponse)) => void;
30
- helpers: {
31
- /**
32
- * Fetch all active queries.
33
- *
34
- * Same as `fetch` method, this will only be called if the data is stale or empty.
35
- */
36
- fetchAllActiveQueries: () => void;
37
- /**
38
- * Fetch all active queries.
39
- *
40
- * Same as `fetch` method, this will only be called if the data is stale or empty.
41
- */
42
- forceFetchAllActiveQueries: () => void;
43
- /**
44
- * Delete query data for all query keys.
45
- */
46
- resetAllQueries: () => void;
47
- };
26
+ * Set query state (data, error, etc) to initial state.
27
+ */
28
+ reset: () => void;
48
29
  /**
49
30
  * Network fetching status.
50
31
  */
@@ -84,6 +65,7 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
84
65
  isRefetching: boolean;
85
66
  isRefetchError: boolean;
86
67
  isPreviousData: boolean;
68
+ isOptimisticData: boolean;
87
69
  data: TData | null;
88
70
  response: TResponse | null;
89
71
  responseUpdatedAt: number | null;
@@ -159,4 +141,33 @@ export type CreateQueryOptions<TKey extends StoreKey = StoreKey, TResponse = any
159
141
  onError?: (error: TError, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
160
142
  onSettled?: (stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
161
143
  };
162
- export declare const createQuery: <TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown>(queryFn: (key: TKey, state: QueryState<TKey, TResponse, TData, TError>) => Promise<TResponse>, options?: CreateQueryOptions<TKey, TResponse, TData, TError>) => import("./create-stores").UseStores<TKey, QueryState<TKey, TResponse, TData, TError>>;
144
+ export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = UseStores<TKey, QueryState<TKey, TResponse, TData, TError>> & {
145
+ /**
146
+ * Set query's initial response.
147
+ *
148
+ * This is used for server-side rendered page or static page.
149
+ *
150
+ * IMPORTANT NOTE: Put this on the root component or parent component, before any component subscribed!
151
+ */
152
+ setInitialResponse: (options: {
153
+ key?: TKey;
154
+ response: TResponse;
155
+ }) => void;
156
+ invalidate: () => void;
157
+ invalidateSpecificKey: (key?: TKey | null) => void;
158
+ /**
159
+ * Optimistic update.
160
+ *
161
+ * @returns function to revert the changes & function to invalidate the query
162
+ *
163
+ * IMPORTANT NOTE: This won't work well on infinite query.
164
+ */
165
+ optimisticUpdate: (options: {
166
+ key?: TKey | null;
167
+ response: TResponse | ((prevState: QueryState<TKey, TResponse, TData, TError>) => TResponse);
168
+ }) => {
169
+ revert: () => void;
170
+ invalidate: () => void;
171
+ };
172
+ };
173
+ export declare const createQuery: <TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown>(queryFn: (key: TKey, state: QueryState<TKey, TResponse, TData, TError>) => Promise<TResponse>, options?: CreateQueryOptions<TKey, TResponse, TData, TError>) => UseQuery<TKey, TResponse, TData, TError>;
@@ -1,3 +1,4 @@
1
+ import { useState } from 'react';
1
2
  import { hashStoreKey, identityFn, noop } from '../utils';
2
3
  import { createStores } from './create-stores';
3
4
  const getDecision = (value, param, { ifTrue, ifAlways }) => {
@@ -19,6 +20,7 @@ const INITIAL_QUERY_STATE = {
19
20
  isRefetching: false,
20
21
  isRefetchError: false,
21
22
  isPreviousData: false,
23
+ isOptimisticData: false,
22
24
  data: null,
23
25
  response: null,
24
26
  responseUpdatedAt: null,
@@ -89,6 +91,7 @@ export const createQuery = (queryFn, options = {}) => {
89
91
  isRefetching: false,
90
92
  isRefetchError: false,
91
93
  isPreviousData: false,
94
+ isOptimisticData: false,
92
95
  data: responseAllPages.reduce((prev, responseCurrentPage) => {
93
96
  return select(responseCurrentPage, { key, data: prev });
94
97
  }, null),
@@ -191,38 +194,6 @@ export const createQuery = (queryFn, options = {}) => {
191
194
  }
192
195
  });
193
196
  };
194
- const setResponse = (responseSetter) => {
195
- if (responseSetter === undefined)
196
- return;
197
- const store = useQuery.get(key);
198
- const response = typeof responseSetter === 'function'
199
- ? responseSetter(store)
200
- : responseSetter;
201
- useQuery.set(key, {
202
- status: 'success',
203
- isLoading: false,
204
- isSuccess: true,
205
- isError: false,
206
- response,
207
- responseUpdatedAt: Date.now(),
208
- data: select(response, { key, data: store.data }),
209
- });
210
- };
211
- const fetchAllActiveQueries = () => {
212
- useQuery.getAllWithSubscriber().forEach((state) => {
213
- state.fetch();
214
- });
215
- };
216
- const forceFetchAllActiveQueries = () => {
217
- useQuery.getAllWithSubscriber().forEach((state) => {
218
- state.forceFetch();
219
- });
220
- };
221
- const resetAllQueries = () => {
222
- useQuery.getAll().forEach((state) => {
223
- state.reset();
224
- });
225
- };
226
197
  return {
227
198
  ...INITIAL_QUERY_STATE,
228
199
  key,
@@ -231,12 +202,6 @@ export const createQuery = (queryFn, options = {}) => {
231
202
  fetchNextPage,
232
203
  markAsStale: () => set({ responseUpdatedAt: null }),
233
204
  reset: () => set(INITIAL_QUERY_STATE),
234
- setResponse,
235
- helpers: {
236
- fetchAllActiveQueries,
237
- forceFetchAllActiveQueries,
238
- resetAllQueries,
239
- },
240
205
  };
241
206
  }, (() => {
242
207
  const fetchWindowFocusHandler = () => {
@@ -292,5 +257,59 @@ export const createQuery = (queryFn, options = {}) => {
292
257
  },
293
258
  };
294
259
  })());
260
+ useQuery.setInitialResponse = ({ key, response }) => {
261
+ // eslint-disable-next-line react-hooks/rules-of-hooks
262
+ useState(() => {
263
+ if (response === undefined)
264
+ return;
265
+ const newPageParam = getNextPageParam(response, 1);
266
+ useQuery.set(key, {
267
+ status: 'success',
268
+ isLoading: false,
269
+ isSuccess: true,
270
+ isError: false,
271
+ response,
272
+ responseUpdatedAt: Date.now(),
273
+ data: select(response, { key: key, data: null }),
274
+ pageParam: newPageParam,
275
+ pageParams: [undefined, newPageParam],
276
+ hasNextPage: newPageParam !== undefined,
277
+ });
278
+ });
279
+ };
280
+ useQuery.invalidate = () => {
281
+ useQuery.getAll().forEach(({ key }) => {
282
+ useQuery.get(key).markAsStale();
283
+ });
284
+ useQuery.getAllWithSubscriber().forEach(({ key }) => {
285
+ useQuery.get(key).forceFetch();
286
+ });
287
+ };
288
+ useQuery.invalidateSpecificKey = (key) => {
289
+ useQuery.get(key).markAsStale();
290
+ const { getSubscribers } = useQuery.getStore(key);
291
+ if (getSubscribers().size > 0)
292
+ useQuery.get(key).forceFetch();
293
+ };
294
+ useQuery.optimisticUpdate = ({ key, response }) => {
295
+ const prevState = useQuery.get(key);
296
+ const optimisticResponse = typeof response === 'function'
297
+ ? response(prevState)
298
+ : response;
299
+ useQuery.set(key, {
300
+ isOptimisticData: true,
301
+ response: optimisticResponse,
302
+ data: select(optimisticResponse, { key: key, data: null }),
303
+ });
304
+ const revert = () => {
305
+ useQuery.set(key, {
306
+ isOptimisticData: false,
307
+ response: prevState.response,
308
+ data: prevState.data,
309
+ });
310
+ };
311
+ const invalidate = () => useQuery.invalidateSpecificKey(key);
312
+ return { revert, invalidate };
313
+ };
295
314
  return useQuery;
296
315
  };
@@ -1,4 +1,4 @@
1
- import { InitStoreOptions, SelectDeps, SetStoreData, StoreData, Subscribers } from '../vanilla';
1
+ import { InitStoreOptions, InitStoreReturn, SelectDeps, SetStoreData, StoreData, Subscribers } from '../vanilla';
2
2
  import { WatchProps } from './create-store';
3
3
  type Maybe<T> = T | null | undefined;
4
4
  export type StoreKey = Record<string, any> | undefined;
@@ -24,6 +24,7 @@ export type UseStores<TKey extends StoreKey = StoreKey, T extends StoreData = St
24
24
  setAll: (value: SetStoreData<T>, silent?: boolean) => void;
25
25
  subscribe: (key: Maybe<TKey>, fn: (state: T) => void, selectDeps?: SelectDeps<T>) => () => void;
26
26
  getSubscribers: (key: Maybe<TKey>) => Subscribers<T>;
27
+ getStore: (key?: Maybe<TKey>) => InitStoreReturn<T>;
27
28
  /**
28
29
  * Set default values inside a component.
29
30
  *
@@ -74,6 +74,7 @@ export const createStores = (initializer, options = {}) => {
74
74
  const store = getStore(key);
75
75
  return store.getSubscribers();
76
76
  };
77
+ useStores.getStore = (key) => getStore(key);
77
78
  useStores.setDefaultValues = (key, value) => {
78
79
  // eslint-disable-next-line react-hooks/rules-of-hooks
79
80
  useState(() => {
@@ -1,4 +1,4 @@
1
- import { CreateStoresOptions, StoreKey } from './create-stores';
1
+ import { CreateStoresOptions, StoreKey, UseStores } from './create-stores';
2
2
  export type QueryStatus = 'loading' | 'success' | 'error';
3
3
  export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = {
4
4
  /**
@@ -22,29 +22,10 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
22
22
  */
23
23
  fetchNextPage: () => void;
24
24
  markAsStale: () => void;
25
- reset: () => void;
26
25
  /**
27
- * Set query response & data.
28
- */
29
- setResponse: (responseSetter: TResponse | ((state: QueryState<TKey, TResponse, TData, TError>) => TResponse)) => void;
30
- helpers: {
31
- /**
32
- * Fetch all active queries.
33
- *
34
- * Same as `fetch` method, this will only be called if the data is stale or empty.
35
- */
36
- fetchAllActiveQueries: () => void;
37
- /**
38
- * Fetch all active queries.
39
- *
40
- * Same as `fetch` method, this will only be called if the data is stale or empty.
41
- */
42
- forceFetchAllActiveQueries: () => void;
43
- /**
44
- * Delete query data for all query keys.
45
- */
46
- resetAllQueries: () => void;
47
- };
26
+ * Set query state (data, error, etc) to initial state.
27
+ */
28
+ reset: () => void;
48
29
  /**
49
30
  * Network fetching status.
50
31
  */
@@ -84,6 +65,7 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
84
65
  isRefetching: boolean;
85
66
  isRefetchError: boolean;
86
67
  isPreviousData: boolean;
68
+ isOptimisticData: boolean;
87
69
  data: TData | null;
88
70
  response: TResponse | null;
89
71
  responseUpdatedAt: number | null;
@@ -159,4 +141,33 @@ export type CreateQueryOptions<TKey extends StoreKey = StoreKey, TResponse = any
159
141
  onError?: (error: TError, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
160
142
  onSettled?: (stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
161
143
  };
162
- export declare const createQuery: <TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown>(queryFn: (key: TKey, state: QueryState<TKey, TResponse, TData, TError>) => Promise<TResponse>, options?: CreateQueryOptions<TKey, TResponse, TData, TError>) => import("./create-stores").UseStores<TKey, QueryState<TKey, TResponse, TData, TError>>;
144
+ export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = UseStores<TKey, QueryState<TKey, TResponse, TData, TError>> & {
145
+ /**
146
+ * Set query's initial response.
147
+ *
148
+ * This is used for server-side rendered page or static page.
149
+ *
150
+ * IMPORTANT NOTE: Put this on the root component or parent component, before any component subscribed!
151
+ */
152
+ setInitialResponse: (options: {
153
+ key?: TKey;
154
+ response: TResponse;
155
+ }) => void;
156
+ invalidate: () => void;
157
+ invalidateSpecificKey: (key?: TKey | null) => void;
158
+ /**
159
+ * Optimistic update.
160
+ *
161
+ * @returns function to revert the changes & function to invalidate the query
162
+ *
163
+ * IMPORTANT NOTE: This won't work well on infinite query.
164
+ */
165
+ optimisticUpdate: (options: {
166
+ key?: TKey | null;
167
+ response: TResponse | ((prevState: QueryState<TKey, TResponse, TData, TError>) => TResponse);
168
+ }) => {
169
+ revert: () => void;
170
+ invalidate: () => void;
171
+ };
172
+ };
173
+ export declare const createQuery: <TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown>(queryFn: (key: TKey, state: QueryState<TKey, TResponse, TData, TError>) => Promise<TResponse>, options?: CreateQueryOptions<TKey, TResponse, TData, TError>) => UseQuery<TKey, TResponse, TData, TError>;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createQuery = void 0;
4
+ const react_1 = require("react");
4
5
  const utils_1 = require("../utils");
5
6
  const create_stores_1 = require("./create-stores");
6
7
  const getDecision = (value, param, { ifTrue, ifAlways }) => {
@@ -22,6 +23,7 @@ const INITIAL_QUERY_STATE = {
22
23
  isRefetching: false,
23
24
  isRefetchError: false,
24
25
  isPreviousData: false,
26
+ isOptimisticData: false,
25
27
  data: null,
26
28
  response: null,
27
29
  responseUpdatedAt: null,
@@ -92,6 +94,7 @@ const createQuery = (queryFn, options = {}) => {
92
94
  isRefetching: false,
93
95
  isRefetchError: false,
94
96
  isPreviousData: false,
97
+ isOptimisticData: false,
95
98
  data: responseAllPages.reduce((prev, responseCurrentPage) => {
96
99
  return select(responseCurrentPage, { key, data: prev });
97
100
  }, null),
@@ -194,38 +197,6 @@ const createQuery = (queryFn, options = {}) => {
194
197
  }
195
198
  });
196
199
  };
197
- const setResponse = (responseSetter) => {
198
- if (responseSetter === undefined)
199
- return;
200
- const store = useQuery.get(key);
201
- const response = typeof responseSetter === 'function'
202
- ? responseSetter(store)
203
- : responseSetter;
204
- useQuery.set(key, {
205
- status: 'success',
206
- isLoading: false,
207
- isSuccess: true,
208
- isError: false,
209
- response,
210
- responseUpdatedAt: Date.now(),
211
- data: select(response, { key, data: store.data }),
212
- });
213
- };
214
- const fetchAllActiveQueries = () => {
215
- useQuery.getAllWithSubscriber().forEach((state) => {
216
- state.fetch();
217
- });
218
- };
219
- const forceFetchAllActiveQueries = () => {
220
- useQuery.getAllWithSubscriber().forEach((state) => {
221
- state.forceFetch();
222
- });
223
- };
224
- const resetAllQueries = () => {
225
- useQuery.getAll().forEach((state) => {
226
- state.reset();
227
- });
228
- };
229
200
  return {
230
201
  ...INITIAL_QUERY_STATE,
231
202
  key,
@@ -234,12 +205,6 @@ const createQuery = (queryFn, options = {}) => {
234
205
  fetchNextPage,
235
206
  markAsStale: () => set({ responseUpdatedAt: null }),
236
207
  reset: () => set(INITIAL_QUERY_STATE),
237
- setResponse,
238
- helpers: {
239
- fetchAllActiveQueries,
240
- forceFetchAllActiveQueries,
241
- resetAllQueries,
242
- },
243
208
  };
244
209
  }, (() => {
245
210
  const fetchWindowFocusHandler = () => {
@@ -295,6 +260,60 @@ const createQuery = (queryFn, options = {}) => {
295
260
  },
296
261
  };
297
262
  })());
263
+ useQuery.setInitialResponse = ({ key, response }) => {
264
+ // eslint-disable-next-line react-hooks/rules-of-hooks
265
+ (0, react_1.useState)(() => {
266
+ if (response === undefined)
267
+ return;
268
+ const newPageParam = getNextPageParam(response, 1);
269
+ useQuery.set(key, {
270
+ status: 'success',
271
+ isLoading: false,
272
+ isSuccess: true,
273
+ isError: false,
274
+ response,
275
+ responseUpdatedAt: Date.now(),
276
+ data: select(response, { key: key, data: null }),
277
+ pageParam: newPageParam,
278
+ pageParams: [undefined, newPageParam],
279
+ hasNextPage: newPageParam !== undefined,
280
+ });
281
+ });
282
+ };
283
+ useQuery.invalidate = () => {
284
+ useQuery.getAll().forEach(({ key }) => {
285
+ useQuery.get(key).markAsStale();
286
+ });
287
+ useQuery.getAllWithSubscriber().forEach(({ key }) => {
288
+ useQuery.get(key).forceFetch();
289
+ });
290
+ };
291
+ useQuery.invalidateSpecificKey = (key) => {
292
+ useQuery.get(key).markAsStale();
293
+ const { getSubscribers } = useQuery.getStore(key);
294
+ if (getSubscribers().size > 0)
295
+ useQuery.get(key).forceFetch();
296
+ };
297
+ useQuery.optimisticUpdate = ({ key, response }) => {
298
+ const prevState = useQuery.get(key);
299
+ const optimisticResponse = typeof response === 'function'
300
+ ? response(prevState)
301
+ : response;
302
+ useQuery.set(key, {
303
+ isOptimisticData: true,
304
+ response: optimisticResponse,
305
+ data: select(optimisticResponse, { key: key, data: null }),
306
+ });
307
+ const revert = () => {
308
+ useQuery.set(key, {
309
+ isOptimisticData: false,
310
+ response: prevState.response,
311
+ data: prevState.data,
312
+ });
313
+ };
314
+ const invalidate = () => useQuery.invalidateSpecificKey(key);
315
+ return { revert, invalidate };
316
+ };
298
317
  return useQuery;
299
318
  };
300
319
  exports.createQuery = createQuery;
@@ -1,4 +1,4 @@
1
- import { InitStoreOptions, SelectDeps, SetStoreData, StoreData, Subscribers } from '../vanilla';
1
+ import { InitStoreOptions, InitStoreReturn, SelectDeps, SetStoreData, StoreData, Subscribers } from '../vanilla';
2
2
  import { WatchProps } from './create-store';
3
3
  type Maybe<T> = T | null | undefined;
4
4
  export type StoreKey = Record<string, any> | undefined;
@@ -24,6 +24,7 @@ export type UseStores<TKey extends StoreKey = StoreKey, T extends StoreData = St
24
24
  setAll: (value: SetStoreData<T>, silent?: boolean) => void;
25
25
  subscribe: (key: Maybe<TKey>, fn: (state: T) => void, selectDeps?: SelectDeps<T>) => () => void;
26
26
  getSubscribers: (key: Maybe<TKey>) => Subscribers<T>;
27
+ getStore: (key?: Maybe<TKey>) => InitStoreReturn<T>;
27
28
  /**
28
29
  * Set default values inside a component.
29
30
  *
@@ -77,6 +77,7 @@ const createStores = (initializer, options = {}) => {
77
77
  const store = getStore(key);
78
78
  return store.getSubscribers();
79
79
  };
80
+ useStores.getStore = (key) => getStore(key);
80
81
  useStores.setDefaultValues = (key, value) => {
81
82
  // eslint-disable-next-line react-hooks/rules-of-hooks
82
83
  (0, react_1.useState)(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "floppy-disk",
3
- "version": "1.4.1-beta.1",
3
+ "version": "2.0.0-alpha.1",
4
4
  "description": "FloppyDisk - lightweight, simple, and powerful state management library",
5
5
  "keywords": [
6
6
  "state",