floppy-disk 2.12.2 → 2.13.0
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 +39 -19
- package/esm/react/create-bi-direction-query.d.ts +1 -2
- package/esm/react/create-store.d.ts +23 -7
- package/esm/react/create-store.js +2 -2
- package/esm/react/create-stores.d.ts +24 -4
- package/esm/react/create-stores.js +3 -2
- package/esm/vanilla.d.ts +1 -1
- package/esm/vanilla.js +1 -1
- package/lib/react/create-bi-direction-query.d.ts +1 -2
- package/lib/react/create-store.d.ts +23 -7
- package/lib/react/create-store.js +2 -2
- package/lib/react/create-stores.d.ts +24 -4
- package/lib/react/create-stores.js +3 -2
- package/lib/vanilla.d.ts +1 -1
- package/lib/vanilla.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Both are awesome state manager. That's why this Floppy Disk library behaves like
|
|
|
9
9
|
|
|
10
10
|
```js
|
|
11
11
|
import { create } from 'zustand'; // 3.3 kB (gzipped: 1.5 kB)
|
|
12
|
-
import { createStore } from 'floppy-disk'; // 1.4 kB (gzipped:
|
|
12
|
+
import { createStore } from 'floppy-disk'; // 1.4 kB (gzipped: 750 B) 🎉
|
|
13
13
|
|
|
14
14
|
import {
|
|
15
15
|
QueryClient,
|
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
useQuery,
|
|
18
18
|
useInfiniteQuery,
|
|
19
19
|
useMutation,
|
|
20
|
-
} from '@tanstack/react-query'; //
|
|
21
|
-
import { createQuery, createMutation } from 'floppy-disk'; // 9.
|
|
20
|
+
} from '@tanstack/react-query'; // 31.7 kB kB (gzipped: 9.2 kB)
|
|
21
|
+
import { createQuery, createMutation } from 'floppy-disk'; // 9.7 kB (gzipped: 3.3 kB) 🎉
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
- Using Zustand & React-Query: https://demo-zustand-react-query.vercel.app/
|
|
@@ -30,7 +30,7 @@ import { createQuery, createMutation } from 'floppy-disk'; // 9.6 kB (gzipped: 3
|
|
|
30
30
|
|
|
31
31
|
- **Create Store**
|
|
32
32
|
- Get/set store inside/outside component
|
|
33
|
-
-
|
|
33
|
+
- Very simple way to customize the reactivity (state update subscription)
|
|
34
34
|
- Support middleware
|
|
35
35
|
- Set state interception
|
|
36
36
|
- Store event (`onSubscribe`, `onUnsubscribe`, etc.)
|
|
@@ -112,12 +112,12 @@ Use the hook anywhere, no providers are needed.
|
|
|
112
112
|
|
|
113
113
|
```jsx
|
|
114
114
|
function Cat() {
|
|
115
|
-
const
|
|
115
|
+
const age = useCatStore('age');
|
|
116
116
|
return <div>Cat's age: {age}</div>;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
function Control() {
|
|
120
|
-
const
|
|
120
|
+
const increaseAge = useCatStore('increaseAge');
|
|
121
121
|
return <button onClick={increaseAge}>Increase cat's age</button>;
|
|
122
122
|
}
|
|
123
123
|
```
|
|
@@ -127,29 +127,39 @@ function Control() {
|
|
|
127
127
|
Control the reactivity. The concept is same as useEffect dependency array.
|
|
128
128
|
|
|
129
129
|
```jsx
|
|
130
|
-
function
|
|
130
|
+
function YourComponent() {
|
|
131
131
|
const { age, isSleeping } = useCatStore();
|
|
132
132
|
// Will re-render every state change ^
|
|
133
|
-
return <div>...</div>;
|
|
134
133
|
}
|
|
135
134
|
|
|
136
|
-
function
|
|
135
|
+
function YourComponent() {
|
|
137
136
|
const { age, isSleeping } = useCatStore((state) => [state.isSleeping]);
|
|
138
137
|
// Will only re-render when isSleeping is updated ^
|
|
139
138
|
// Update on age won't cause re-render this component
|
|
140
|
-
return <div>...</div>;
|
|
141
139
|
}
|
|
142
140
|
|
|
143
|
-
function
|
|
141
|
+
function YourComponent() {
|
|
144
142
|
const { age, isSleeping } = useCatStore((state) => [state.age, state.isSleeping]);
|
|
145
143
|
// Will re-render when age or isSleeping is updated ^
|
|
146
|
-
return <div>...</div>;
|
|
147
144
|
}
|
|
148
145
|
|
|
149
|
-
function
|
|
146
|
+
function YourComponent() {
|
|
150
147
|
const { age, isSleeping } = useCatStore((state) => [state.age > 3]);
|
|
151
148
|
// Will only re-render when (age>3) is updated
|
|
152
|
-
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Even simpler way, after version `2.13.0`, we can use store's object key:
|
|
153
|
+
|
|
154
|
+
```jsx
|
|
155
|
+
function YourComponent() {
|
|
156
|
+
const age = useCatStore('age');
|
|
157
|
+
// Will only re-render when age is updated
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function YourComponent() {
|
|
161
|
+
const age = useCatStore('isSleeping');
|
|
162
|
+
// Will only re-render when isSleeping is updated
|
|
153
163
|
}
|
|
154
164
|
```
|
|
155
165
|
|
|
@@ -188,7 +198,7 @@ const decreaseAgeSilently = () => {
|
|
|
188
198
|
};
|
|
189
199
|
// 👇 Will not re-render
|
|
190
200
|
function Cat() {
|
|
191
|
-
const
|
|
201
|
+
const age = useCatStore('age');
|
|
192
202
|
return <div>Cat's age: {age}</div>;
|
|
193
203
|
}
|
|
194
204
|
```
|
|
@@ -258,7 +268,7 @@ Prevent re-render using `Watch`.
|
|
|
258
268
|
|
|
259
269
|
```jsx
|
|
260
270
|
function CatPage() {
|
|
261
|
-
const
|
|
271
|
+
const age = useCatStore('age');
|
|
262
272
|
// If age changed, this component will re-render which will cause
|
|
263
273
|
// HeavyComponent1 & HeavyComponent2 to be re-rendered as well.
|
|
264
274
|
return (
|
|
@@ -276,8 +286,8 @@ function CatPageOptimized() {
|
|
|
276
286
|
<main>
|
|
277
287
|
<HeavyComponent1 />
|
|
278
288
|
<useCatStore.Watch
|
|
279
|
-
selectDeps=
|
|
280
|
-
render={(
|
|
289
|
+
selectDeps="age"
|
|
290
|
+
render={(age) => {
|
|
281
291
|
return <div>Cat's age: {age}</div>;
|
|
282
292
|
}}
|
|
283
293
|
/>
|
|
@@ -328,11 +338,20 @@ function Parent() {
|
|
|
328
338
|
|
|
329
339
|
function CatAge() {
|
|
330
340
|
const { age } = useCatStoreContext()((state) => [state.age]);
|
|
341
|
+
|
|
342
|
+
// Shorthand after v1.13.0:
|
|
343
|
+
// const age = useCatStoreContext()('age');
|
|
344
|
+
|
|
331
345
|
return <div>Age: {age}</div>;
|
|
332
346
|
}
|
|
347
|
+
|
|
333
348
|
function CatIsSleeping() {
|
|
334
349
|
const useCatStore = useCatStoreContext();
|
|
335
350
|
const { isSleeping } = useCatStore((state) => [state.isSleeping]);
|
|
351
|
+
|
|
352
|
+
// Shorthand after v1.13.0:
|
|
353
|
+
// const isSleeping = useCatStore('isSleeping');
|
|
354
|
+
|
|
336
355
|
return (
|
|
337
356
|
<>
|
|
338
357
|
<div>Is Sleeping: {String(isSleeping)}</div>
|
|
@@ -764,7 +783,7 @@ function SaveProduct() {
|
|
|
764
783
|
|
|
765
784
|
## Important Notes
|
|
766
785
|
|
|
767
|
-
Don't mutate.
|
|
786
|
+
Don't mutate. (unless you use Immer JS library or something similar)
|
|
768
787
|
|
|
769
788
|
```js
|
|
770
789
|
import { createStore } from 'floppy-disk';
|
|
@@ -783,6 +802,7 @@ Don't use conditional reactivity selector.
|
|
|
783
802
|
|
|
784
803
|
```jsx
|
|
785
804
|
function Cat({ isSomething }) {
|
|
805
|
+
const value = useCatStore(isSomething ? 'age' : 'isSleeping'); // ❌
|
|
786
806
|
const { age } = useCatStore(isSomething ? (state) => [state.age] : null); // ❌
|
|
787
807
|
const { age } = useCatStore((state) => (isSomething ? [state.age] : [state.isSleeping])); // ❌
|
|
788
808
|
return <div>Cat's age: {age}</div>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Maybe } from '../utils';
|
|
2
|
-
import { SelectDeps } from '../vanilla';
|
|
3
2
|
import { CreateQueryOptions, QueryState } from './create-query';
|
|
4
3
|
import { StoreKey } from './create-stores';
|
|
5
4
|
export declare const createBiDirectionQuery: <TKey extends StoreKey = StoreKey, TResponse = any, TData extends any[] = any[], TError = unknown, TPageParam = any>(queryFn: (key: TKey, state: QueryState<TKey, TResponse, TData, TError, TPageParam>, direction: 'prev' | 'next') => Promise<TResponse>, options: Omit<CreateQueryOptions<TKey, TResponse, TData, TError, TPageParam>, "select" | "getNextPageParam"> & {
|
|
@@ -7,7 +6,7 @@ export declare const createBiDirectionQuery: <TKey extends StoreKey = StoreKey,
|
|
|
7
6
|
getNextPageParam: (lastPage: TResponse, index: number, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError, TPageParam>) => Maybe<TPageParam>;
|
|
8
7
|
select: (response: TResponse, state: Pick<QueryState<TKey, TResponse, TData, TError, TPageParam>, "data" | "key">, direction: 'prev' | 'next') => TData;
|
|
9
8
|
}) => {
|
|
10
|
-
(
|
|
9
|
+
(key: Maybe<TKey>, selectDeps?: "data" | "key" | "error" | "reset" | "status" | "fetch" | "response" | "keyHash" | "forceFetch" | "fetchNextPage" | "optimisticUpdate" | "isWaiting" | "isWaitingNextPage" | "isRefetching" | "isRefetchError" | "isPreviousData" | "isOptimisticData" | "errorUpdatedAt" | "retryCount" | "isGoingToRetry" | "pageParam" | "pageParams" | "hasNextPage" | "retryNextPageCount" | "isGoingToRetryNextPage" | "isLoading" | "isSuccess" | "isError" | "responseUpdatedAt" | import("..").SelectDeps<QueryState<TKey, TResponse, TData, TError, TPageParam>>): {
|
|
11
10
|
data: (never[] | TData)[number][];
|
|
12
11
|
fetchPrevPage: () => Promise<QueryState<TKey, TResponse, TData, TError, TPageParam>>;
|
|
13
12
|
hasPrevPage: boolean;
|
|
@@ -1,19 +1,35 @@
|
|
|
1
1
|
import { InitStoreOptions, SelectDeps, SetStoreData, StoreData, StoreInitializer, Subscribers } from '../vanilla';
|
|
2
|
-
export type WatchProps<T> = {
|
|
3
|
-
selectDeps?:
|
|
4
|
-
render: (state: T) => any;
|
|
2
|
+
export type WatchProps<T, K extends SelectDeps<T> | keyof T = SelectDeps<T>> = {
|
|
3
|
+
selectDeps?: K;
|
|
4
|
+
render: (state: K extends keyof T ? T[K] : T) => any;
|
|
5
5
|
};
|
|
6
6
|
export type UseStore<T extends StoreData> = {
|
|
7
7
|
/**
|
|
8
|
-
* @param selectDeps A function that return the dependency array (just like in `useEffect`), to trigger reactivity.
|
|
8
|
+
* @param selectDeps (Optional) A function that return the dependency array (just like in `useEffect`), to trigger reactivity.
|
|
9
9
|
* Defaults to `undefined` (reactive to all state change) if you didn't set `defaultDeps` on `createStore`.
|
|
10
10
|
*
|
|
11
|
+
* Since version `2.13.0`, we can use a store's object key to control reactivity.
|
|
12
|
+
*
|
|
11
13
|
* **IMPORTANT NOTE:** `selectDeps` must not be changed after initialization.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const useMyStore = createStore({
|
|
18
|
+
* foo: 12,
|
|
19
|
+
* bar: true,
|
|
20
|
+
* baz: "z",
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* const MyComponent = () => {
|
|
24
|
+
* const foo = useMyStore("foo");
|
|
25
|
+
* // Will only re-render if "foo" updated
|
|
26
|
+
* };
|
|
27
|
+
* ```
|
|
12
28
|
*/
|
|
13
|
-
|
|
29
|
+
<K extends SelectDeps<T> | keyof T = SelectDeps<T>>(selectDeps?: K): K extends keyof T ? T[K] : T;
|
|
14
30
|
get: () => T;
|
|
15
31
|
set: (value: SetStoreData<T>, silent?: boolean) => void;
|
|
16
|
-
subscribe: (fn: (state: T) => void, selectDeps?: SelectDeps<T>) => () => void;
|
|
32
|
+
subscribe: (fn: (state: T) => void, selectDeps?: SelectDeps<T> | keyof T) => () => void;
|
|
17
33
|
getSubscribers: () => Subscribers<T>;
|
|
18
34
|
/**
|
|
19
35
|
* ⚛️ (**_Hook_**)
|
|
@@ -25,7 +41,7 @@ export type UseStore<T extends StoreData> = {
|
|
|
25
41
|
* - Put this on the root component or parent component, before any component subscribed!
|
|
26
42
|
*/
|
|
27
43
|
setDefaultValues: (values: SetStoreData<T>) => void;
|
|
28
|
-
Watch: (props: WatchProps<T>) => any;
|
|
44
|
+
Watch: <K extends SelectDeps<T> | keyof T = SelectDeps<T>>(props: WatchProps<T, K>) => any;
|
|
29
45
|
};
|
|
30
46
|
/**
|
|
31
47
|
* @see https://floppy-disk.vercel.app/docs/api#createstore
|
|
@@ -13,7 +13,7 @@ export const createStore = (initializer, options = {}) => {
|
|
|
13
13
|
const [state, setState] = useState(get);
|
|
14
14
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
15
15
|
useEffect(() => subscribe(setState, selectDeps), []);
|
|
16
|
-
return state;
|
|
16
|
+
return (typeof selectDeps === 'string' ? state[selectDeps] : state);
|
|
17
17
|
};
|
|
18
18
|
useStore.get = get;
|
|
19
19
|
useStore.set = set;
|
|
@@ -29,7 +29,7 @@ export const createStore = (initializer, options = {}) => {
|
|
|
29
29
|
set(value);
|
|
30
30
|
});
|
|
31
31
|
};
|
|
32
|
-
const Watch = ({ selectDeps = defaultDeps, render }) => {
|
|
32
|
+
const Watch = ({ selectDeps = defaultDeps, render, }) => {
|
|
33
33
|
const store = useStore(selectDeps);
|
|
34
34
|
return render(store);
|
|
35
35
|
};
|
|
@@ -11,19 +11,39 @@ export type StoresInitializer<TKey extends StoreKey = StoreKey, T extends StoreD
|
|
|
11
11
|
export type UseStores<TKey extends StoreKey = StoreKey, T extends StoreData = StoreData> = {
|
|
12
12
|
/**
|
|
13
13
|
* @param key (Optional) Store key, an object that will be hashed into a string as a store identifier.
|
|
14
|
+
* No need to memoize the store key.
|
|
14
15
|
*
|
|
15
|
-
* @param selectDeps A function that return the dependency array (just like in `useEffect`), to trigger reactivity.
|
|
16
|
+
* @param selectDeps (Optional) A function that return the dependency array (just like in `useEffect`), to trigger reactivity.
|
|
16
17
|
* Defaults to `undefined` (reactive to all state change) if you didn't set `defaultDeps` on `createStores`.
|
|
17
18
|
*
|
|
19
|
+
* Since version `2.13.0`, we can use a store's object key to control reactivity.
|
|
20
|
+
*
|
|
18
21
|
* **IMPORTANT NOTE:** `selectDeps` must not be changed after initialization.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* type StoreKey = { id: string };
|
|
26
|
+
* type StoreData = { foo: number; bar: boolean; baz: string };
|
|
27
|
+
* const useMyStores = createStores<StoreKey, StoreData>({
|
|
28
|
+
* foo: 12,
|
|
29
|
+
* bar: true,
|
|
30
|
+
* baz: "z",
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* export const MyComponent = () => {
|
|
34
|
+
* const foo = useMyStores({ id: "p1" }, "foo");
|
|
35
|
+
* // Will only re-render if "foo" & store key ("id") updated
|
|
36
|
+
* };
|
|
37
|
+
* ```
|
|
19
38
|
*/
|
|
20
|
-
|
|
39
|
+
<K extends SelectDeps<T> | keyof T = SelectDeps<T>>(selectDeps?: K): K extends keyof T ? T[K] : T;
|
|
40
|
+
<K extends SelectDeps<T> | keyof T = SelectDeps<T>>(key: Maybe<TKey>, selectDeps?: K): K extends keyof T ? T[K] : T;
|
|
21
41
|
get: (key?: Maybe<TKey>) => T;
|
|
22
42
|
getAll: () => T[];
|
|
23
43
|
getAllWithSubscriber: () => T[];
|
|
24
44
|
set: (key: Maybe<TKey>, value: SetStoreData<T>, silent?: boolean) => void;
|
|
25
45
|
setAll: (value: SetStoreData<T>, silent?: boolean) => void;
|
|
26
|
-
subscribe: (key: Maybe<TKey>, fn: (state: T) => void, selectDeps?: SelectDeps<T>) => () => void;
|
|
46
|
+
subscribe: (key: Maybe<TKey>, fn: (state: T) => void, selectDeps?: SelectDeps<T> | keyof T) => () => void;
|
|
27
47
|
getSubscribers: (key: Maybe<TKey>) => Subscribers<T>;
|
|
28
48
|
getStore: (key?: Maybe<TKey>) => InitStoreReturn<T>;
|
|
29
49
|
getStores: () => Map<string, InitStoreReturn<T>>;
|
|
@@ -37,7 +57,7 @@ export type UseStores<TKey extends StoreKey = StoreKey, T extends StoreData = St
|
|
|
37
57
|
* - Put this on the root component or parent component, before any component subscribed!
|
|
38
58
|
*/
|
|
39
59
|
setDefaultValues: (key: Maybe<TKey>, values: SetStoreData<T>) => void;
|
|
40
|
-
Watch: (props: WatchProps<T> & {
|
|
60
|
+
Watch: <K extends SelectDeps<T> | keyof T = SelectDeps<T>>(props: WatchProps<T, K> & {
|
|
41
61
|
storeKey?: Maybe<TKey>;
|
|
42
62
|
}) => any;
|
|
43
63
|
};
|
|
@@ -20,7 +20,7 @@ export const createStores = (initializer, options = {}) => {
|
|
|
20
20
|
* **IMPORTANT NOTE:** `selectDeps` function must not be changed after initialization.
|
|
21
21
|
*/
|
|
22
22
|
const useStores = (...args) => {
|
|
23
|
-
const [_key, selectDeps = defaultDeps] = (typeof args[0] === 'function' ? [{}, args[0]] : args);
|
|
23
|
+
const [_key, selectDeps = defaultDeps] = (typeof args[0] === 'function' || typeof args[0] === 'string' ? [{}, args[0]] : args);
|
|
24
24
|
const key = _key || {};
|
|
25
25
|
const keyHash = hashKeyFn(key);
|
|
26
26
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -37,7 +37,8 @@ export const createStores = (initializer, options = {}) => {
|
|
|
37
37
|
}, [keyHash]);
|
|
38
38
|
if (keyHash !== prevKeyHash.current)
|
|
39
39
|
onBeforeChangeKey(key, prevKey.current);
|
|
40
|
-
|
|
40
|
+
const state = get();
|
|
41
|
+
return (typeof selectDeps === 'string' ? state[selectDeps] : state);
|
|
41
42
|
};
|
|
42
43
|
useStores.get = (key) => {
|
|
43
44
|
const store = getStore(key);
|
package/esm/vanilla.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type InitStoreOptions<T> = {
|
|
|
18
18
|
export type InitStoreReturn<T> = {
|
|
19
19
|
get: () => T;
|
|
20
20
|
set: (value: SetStoreData<T>, silent?: boolean) => void;
|
|
21
|
-
subscribe: (fn: (state: T) => void, selectDeps?: SelectDeps<T>) => () => void;
|
|
21
|
+
subscribe: (fn: (state: T) => void, selectDeps?: SelectDeps<T> | keyof T) => () => void;
|
|
22
22
|
getSubscribers: () => Subscribers<T>;
|
|
23
23
|
};
|
|
24
24
|
export declare const initStore: <T extends StoreData>(initializer: StoreInitializer<T>, options?: InitStoreOptions<T>) => InitStoreReturn<T>;
|
package/esm/vanilla.js
CHANGED
|
@@ -35,7 +35,7 @@ export const initStore = (initializer, options = {}) => {
|
|
|
35
35
|
});
|
|
36
36
|
};
|
|
37
37
|
const subscribe = (fn, selectDeps) => {
|
|
38
|
-
subscribers.set(fn, selectDeps);
|
|
38
|
+
subscribers.set(fn, (typeof selectDeps === 'string' ? (s) => [s[selectDeps]] : selectDeps));
|
|
39
39
|
if (subscribers.size === 1)
|
|
40
40
|
onFirstSubscribe(data);
|
|
41
41
|
onSubscribe(data);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Maybe } from '../utils';
|
|
2
|
-
import { SelectDeps } from '../vanilla';
|
|
3
2
|
import { CreateQueryOptions, QueryState } from './create-query';
|
|
4
3
|
import { StoreKey } from './create-stores';
|
|
5
4
|
export declare const createBiDirectionQuery: <TKey extends StoreKey = StoreKey, TResponse = any, TData extends any[] = any[], TError = unknown, TPageParam = any>(queryFn: (key: TKey, state: QueryState<TKey, TResponse, TData, TError, TPageParam>, direction: 'prev' | 'next') => Promise<TResponse>, options: Omit<CreateQueryOptions<TKey, TResponse, TData, TError, TPageParam>, "select" | "getNextPageParam"> & {
|
|
@@ -7,7 +6,7 @@ export declare const createBiDirectionQuery: <TKey extends StoreKey = StoreKey,
|
|
|
7
6
|
getNextPageParam: (lastPage: TResponse, index: number, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError, TPageParam>) => Maybe<TPageParam>;
|
|
8
7
|
select: (response: TResponse, state: Pick<QueryState<TKey, TResponse, TData, TError, TPageParam>, "data" | "key">, direction: 'prev' | 'next') => TData;
|
|
9
8
|
}) => {
|
|
10
|
-
(
|
|
9
|
+
(key: Maybe<TKey>, selectDeps?: "data" | "key" | "error" | "reset" | "status" | "fetch" | "response" | "keyHash" | "forceFetch" | "fetchNextPage" | "optimisticUpdate" | "isWaiting" | "isWaitingNextPage" | "isRefetching" | "isRefetchError" | "isPreviousData" | "isOptimisticData" | "errorUpdatedAt" | "retryCount" | "isGoingToRetry" | "pageParam" | "pageParams" | "hasNextPage" | "retryNextPageCount" | "isGoingToRetryNextPage" | "isLoading" | "isSuccess" | "isError" | "responseUpdatedAt" | import("..").SelectDeps<QueryState<TKey, TResponse, TData, TError, TPageParam>>): {
|
|
11
10
|
data: (never[] | TData)[number][];
|
|
12
11
|
fetchPrevPage: () => Promise<QueryState<TKey, TResponse, TData, TError, TPageParam>>;
|
|
13
12
|
hasPrevPage: boolean;
|
|
@@ -1,19 +1,35 @@
|
|
|
1
1
|
import { InitStoreOptions, SelectDeps, SetStoreData, StoreData, StoreInitializer, Subscribers } from '../vanilla';
|
|
2
|
-
export type WatchProps<T> = {
|
|
3
|
-
selectDeps?:
|
|
4
|
-
render: (state: T) => any;
|
|
2
|
+
export type WatchProps<T, K extends SelectDeps<T> | keyof T = SelectDeps<T>> = {
|
|
3
|
+
selectDeps?: K;
|
|
4
|
+
render: (state: K extends keyof T ? T[K] : T) => any;
|
|
5
5
|
};
|
|
6
6
|
export type UseStore<T extends StoreData> = {
|
|
7
7
|
/**
|
|
8
|
-
* @param selectDeps A function that return the dependency array (just like in `useEffect`), to trigger reactivity.
|
|
8
|
+
* @param selectDeps (Optional) A function that return the dependency array (just like in `useEffect`), to trigger reactivity.
|
|
9
9
|
* Defaults to `undefined` (reactive to all state change) if you didn't set `defaultDeps` on `createStore`.
|
|
10
10
|
*
|
|
11
|
+
* Since version `2.13.0`, we can use a store's object key to control reactivity.
|
|
12
|
+
*
|
|
11
13
|
* **IMPORTANT NOTE:** `selectDeps` must not be changed after initialization.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const useMyStore = createStore({
|
|
18
|
+
* foo: 12,
|
|
19
|
+
* bar: true,
|
|
20
|
+
* baz: "z",
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* const MyComponent = () => {
|
|
24
|
+
* const foo = useMyStore("foo");
|
|
25
|
+
* // Will only re-render if "foo" updated
|
|
26
|
+
* };
|
|
27
|
+
* ```
|
|
12
28
|
*/
|
|
13
|
-
|
|
29
|
+
<K extends SelectDeps<T> | keyof T = SelectDeps<T>>(selectDeps?: K): K extends keyof T ? T[K] : T;
|
|
14
30
|
get: () => T;
|
|
15
31
|
set: (value: SetStoreData<T>, silent?: boolean) => void;
|
|
16
|
-
subscribe: (fn: (state: T) => void, selectDeps?: SelectDeps<T>) => () => void;
|
|
32
|
+
subscribe: (fn: (state: T) => void, selectDeps?: SelectDeps<T> | keyof T) => () => void;
|
|
17
33
|
getSubscribers: () => Subscribers<T>;
|
|
18
34
|
/**
|
|
19
35
|
* ⚛️ (**_Hook_**)
|
|
@@ -25,7 +41,7 @@ export type UseStore<T extends StoreData> = {
|
|
|
25
41
|
* - Put this on the root component or parent component, before any component subscribed!
|
|
26
42
|
*/
|
|
27
43
|
setDefaultValues: (values: SetStoreData<T>) => void;
|
|
28
|
-
Watch: (props: WatchProps<T>) => any;
|
|
44
|
+
Watch: <K extends SelectDeps<T> | keyof T = SelectDeps<T>>(props: WatchProps<T, K>) => any;
|
|
29
45
|
};
|
|
30
46
|
/**
|
|
31
47
|
* @see https://floppy-disk.vercel.app/docs/api#createstore
|
|
@@ -16,7 +16,7 @@ const createStore = (initializer, options = {}) => {
|
|
|
16
16
|
const [state, setState] = (0, react_1.useState)(get);
|
|
17
17
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18
18
|
(0, react_1.useEffect)(() => subscribe(setState, selectDeps), []);
|
|
19
|
-
return state;
|
|
19
|
+
return (typeof selectDeps === 'string' ? state[selectDeps] : state);
|
|
20
20
|
};
|
|
21
21
|
useStore.get = get;
|
|
22
22
|
useStore.set = set;
|
|
@@ -32,7 +32,7 @@ const createStore = (initializer, options = {}) => {
|
|
|
32
32
|
set(value);
|
|
33
33
|
});
|
|
34
34
|
};
|
|
35
|
-
const Watch = ({ selectDeps = defaultDeps, render }) => {
|
|
35
|
+
const Watch = ({ selectDeps = defaultDeps, render, }) => {
|
|
36
36
|
const store = useStore(selectDeps);
|
|
37
37
|
return render(store);
|
|
38
38
|
};
|
|
@@ -11,19 +11,39 @@ export type StoresInitializer<TKey extends StoreKey = StoreKey, T extends StoreD
|
|
|
11
11
|
export type UseStores<TKey extends StoreKey = StoreKey, T extends StoreData = StoreData> = {
|
|
12
12
|
/**
|
|
13
13
|
* @param key (Optional) Store key, an object that will be hashed into a string as a store identifier.
|
|
14
|
+
* No need to memoize the store key.
|
|
14
15
|
*
|
|
15
|
-
* @param selectDeps A function that return the dependency array (just like in `useEffect`), to trigger reactivity.
|
|
16
|
+
* @param selectDeps (Optional) A function that return the dependency array (just like in `useEffect`), to trigger reactivity.
|
|
16
17
|
* Defaults to `undefined` (reactive to all state change) if you didn't set `defaultDeps` on `createStores`.
|
|
17
18
|
*
|
|
19
|
+
* Since version `2.13.0`, we can use a store's object key to control reactivity.
|
|
20
|
+
*
|
|
18
21
|
* **IMPORTANT NOTE:** `selectDeps` must not be changed after initialization.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* type StoreKey = { id: string };
|
|
26
|
+
* type StoreData = { foo: number; bar: boolean; baz: string };
|
|
27
|
+
* const useMyStores = createStores<StoreKey, StoreData>({
|
|
28
|
+
* foo: 12,
|
|
29
|
+
* bar: true,
|
|
30
|
+
* baz: "z",
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* export const MyComponent = () => {
|
|
34
|
+
* const foo = useMyStores({ id: "p1" }, "foo");
|
|
35
|
+
* // Will only re-render if "foo" & store key ("id") updated
|
|
36
|
+
* };
|
|
37
|
+
* ```
|
|
19
38
|
*/
|
|
20
|
-
|
|
39
|
+
<K extends SelectDeps<T> | keyof T = SelectDeps<T>>(selectDeps?: K): K extends keyof T ? T[K] : T;
|
|
40
|
+
<K extends SelectDeps<T> | keyof T = SelectDeps<T>>(key: Maybe<TKey>, selectDeps?: K): K extends keyof T ? T[K] : T;
|
|
21
41
|
get: (key?: Maybe<TKey>) => T;
|
|
22
42
|
getAll: () => T[];
|
|
23
43
|
getAllWithSubscriber: () => T[];
|
|
24
44
|
set: (key: Maybe<TKey>, value: SetStoreData<T>, silent?: boolean) => void;
|
|
25
45
|
setAll: (value: SetStoreData<T>, silent?: boolean) => void;
|
|
26
|
-
subscribe: (key: Maybe<TKey>, fn: (state: T) => void, selectDeps?: SelectDeps<T>) => () => void;
|
|
46
|
+
subscribe: (key: Maybe<TKey>, fn: (state: T) => void, selectDeps?: SelectDeps<T> | keyof T) => () => void;
|
|
27
47
|
getSubscribers: (key: Maybe<TKey>) => Subscribers<T>;
|
|
28
48
|
getStore: (key?: Maybe<TKey>) => InitStoreReturn<T>;
|
|
29
49
|
getStores: () => Map<string, InitStoreReturn<T>>;
|
|
@@ -37,7 +57,7 @@ export type UseStores<TKey extends StoreKey = StoreKey, T extends StoreData = St
|
|
|
37
57
|
* - Put this on the root component or parent component, before any component subscribed!
|
|
38
58
|
*/
|
|
39
59
|
setDefaultValues: (key: Maybe<TKey>, values: SetStoreData<T>) => void;
|
|
40
|
-
Watch: (props: WatchProps<T> & {
|
|
60
|
+
Watch: <K extends SelectDeps<T> | keyof T = SelectDeps<T>>(props: WatchProps<T, K> & {
|
|
41
61
|
storeKey?: Maybe<TKey>;
|
|
42
62
|
}) => any;
|
|
43
63
|
};
|
|
@@ -23,7 +23,7 @@ const createStores = (initializer, options = {}) => {
|
|
|
23
23
|
* **IMPORTANT NOTE:** `selectDeps` function must not be changed after initialization.
|
|
24
24
|
*/
|
|
25
25
|
const useStores = (...args) => {
|
|
26
|
-
const [_key, selectDeps = defaultDeps] = (typeof args[0] === 'function' ? [{}, args[0]] : args);
|
|
26
|
+
const [_key, selectDeps = defaultDeps] = (typeof args[0] === 'function' || typeof args[0] === 'string' ? [{}, args[0]] : args);
|
|
27
27
|
const key = _key || {};
|
|
28
28
|
const keyHash = hashKeyFn(key);
|
|
29
29
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -40,7 +40,8 @@ const createStores = (initializer, options = {}) => {
|
|
|
40
40
|
}, [keyHash]);
|
|
41
41
|
if (keyHash !== prevKeyHash.current)
|
|
42
42
|
onBeforeChangeKey(key, prevKey.current);
|
|
43
|
-
|
|
43
|
+
const state = get();
|
|
44
|
+
return (typeof selectDeps === 'string' ? state[selectDeps] : state);
|
|
44
45
|
};
|
|
45
46
|
useStores.get = (key) => {
|
|
46
47
|
const store = getStore(key);
|
package/lib/vanilla.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type InitStoreOptions<T> = {
|
|
|
18
18
|
export type InitStoreReturn<T> = {
|
|
19
19
|
get: () => T;
|
|
20
20
|
set: (value: SetStoreData<T>, silent?: boolean) => void;
|
|
21
|
-
subscribe: (fn: (state: T) => void, selectDeps?: SelectDeps<T>) => () => void;
|
|
21
|
+
subscribe: (fn: (state: T) => void, selectDeps?: SelectDeps<T> | keyof T) => () => void;
|
|
22
22
|
getSubscribers: () => Subscribers<T>;
|
|
23
23
|
};
|
|
24
24
|
export declare const initStore: <T extends StoreData>(initializer: StoreInitializer<T>, options?: InitStoreOptions<T>) => InitStoreReturn<T>;
|
package/lib/vanilla.js
CHANGED
|
@@ -38,7 +38,7 @@ const initStore = (initializer, options = {}) => {
|
|
|
38
38
|
});
|
|
39
39
|
};
|
|
40
40
|
const subscribe = (fn, selectDeps) => {
|
|
41
|
-
subscribers.set(fn, selectDeps);
|
|
41
|
+
subscribers.set(fn, (typeof selectDeps === 'string' ? (s) => [s[selectDeps]] : selectDeps));
|
|
42
42
|
if (subscribers.size === 1)
|
|
43
43
|
onFirstSubscribe(data);
|
|
44
44
|
onSubscribe(data);
|