@dr.pogodin/react-global-state 0.10.0-alpha.2 → 0.10.0-alpha.3
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 +2 -0
- package/build/common/GlobalState.js +219 -0
- package/build/common/GlobalState.js.map +1 -0
- package/build/common/GlobalStateProvider.js +101 -0
- package/build/common/GlobalStateProvider.js.map +1 -0
- package/build/common/SsrContext.js +15 -0
- package/build/common/SsrContext.js.map +1 -0
- package/build/common/index.js +76 -0
- package/build/common/index.js.map +1 -0
- package/build/common/useAsyncCollection.js +61 -0
- package/build/common/useAsyncCollection.js.map +1 -0
- package/build/common/useAsyncData.js +204 -0
- package/build/common/useAsyncData.js.map +1 -0
- package/build/common/useGlobalState.js +113 -0
- package/build/common/useGlobalState.js.map +1 -0
- package/build/common/utils.js +44 -0
- package/build/common/utils.js.map +1 -0
- package/build/common/withGlobalStateType.js +42 -0
- package/build/common/withGlobalStateType.js.map +1 -0
- package/build/module/GlobalState.js +230 -0
- package/build/module/GlobalState.js.map +1 -0
- package/build/module/GlobalStateProvider.js +94 -0
- package/build/module/GlobalStateProvider.js.map +1 -0
- package/build/module/SsrContext.js +9 -0
- package/build/module/SsrContext.js.map +1 -0
- package/build/module/index.js +8 -0
- package/build/module/index.js.map +1 -0
- package/build/module/useAsyncCollection.js +56 -0
- package/build/module/useAsyncCollection.js.map +1 -0
- package/build/module/useAsyncData.js +199 -0
- package/build/module/useAsyncData.js.map +1 -0
- package/build/module/useGlobalState.js +108 -0
- package/build/module/useGlobalState.js.map +1 -0
- package/build/module/utils.js +35 -0
- package/build/module/utils.js.map +1 -0
- package/build/module/withGlobalStateType.js +35 -0
- package/build/module/withGlobalStateType.js.map +1 -0
- package/build/{GlobalState.d.ts → types/GlobalState.d.ts} +6 -6
- package/build/{GlobalStateProvider.d.ts → types/GlobalStateProvider.d.ts} +8 -8
- package/build/{index.d.ts → types/index.d.ts} +1 -1
- package/build/{useAsyncCollection.d.ts → types/useAsyncCollection.d.ts} +2 -2
- package/build/{useAsyncData.d.ts → types/useAsyncData.d.ts} +2 -3
- package/build/{useGlobalState.d.ts → types/useGlobalState.d.ts} +2 -2
- package/build/{utils.d.ts → types/utils.d.ts} +4 -1
- package/build/types/withGlobalStateType.d.ts +29 -0
- package/package.json +43 -38
- package/src/GlobalState.ts +14 -10
- package/src/GlobalStateProvider.tsx +26 -15
- package/src/index.ts +1 -1
- package/src/useAsyncCollection.ts +5 -5
- package/src/useAsyncData.ts +27 -19
- package/src/useGlobalState.ts +9 -11
- package/src/utils.ts +8 -3
- package/src/withGlobalStateType.ts +77 -25
- package/tsconfig.json +9 -3
- package/tsconfig.types.json +14 -0
- package/build/GlobalState.js +0 -193
- package/build/GlobalStateProvider.js +0 -103
- package/build/SsrContext.js +0 -10
- package/build/index.js +0 -23
- package/build/useAsyncCollection.js +0 -14
- package/build/useAsyncData.js +0 -150
- package/build/useGlobalState.js +0 -49
- package/build/utils.js +0 -25
- package/build/withGlobalStateType.d.ts +0 -39
- package/build/withGlobalStateType.js +0 -55
- /package/build/{SsrContext.d.ts → types/SsrContext.d.ts} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ForceT, type TypeLock, type ValueAtPathT, type ValueOrInitializerT } from './utils';
|
|
2
|
+
import GlobalStateProvider, { getGlobalState, getSsrContext } from './GlobalStateProvider';
|
|
3
|
+
import SsrContext from './SsrContext';
|
|
4
|
+
import { type UseGlobalStateResT } from './useGlobalState';
|
|
5
|
+
import { type AsyncCollectionLoaderT } from './useAsyncCollection';
|
|
6
|
+
import { type AsyncDataLoaderT, type DataInEnvelopeAtPathT, type UseAsyncDataOptionsT, type UseAsyncDataResT } from './useAsyncData';
|
|
7
|
+
interface NarrowedUseGlobalStateI<StateT> {
|
|
8
|
+
(): UseGlobalStateResT<StateT>;
|
|
9
|
+
<PathT extends null | string | undefined>(path: PathT, initialValue?: ValueOrInitializerT<ValueAtPathT<StateT, PathT, never>>): UseGlobalStateResT<ValueAtPathT<StateT, PathT, void>>;
|
|
10
|
+
<Forced extends ForceT | false = false, ValueT = unknown>(path: null | string | undefined, initialValue?: ValueOrInitializerT<TypeLock<Forced, never, ValueT>>): UseGlobalStateResT<TypeLock<Forced, void, ValueT>>;
|
|
11
|
+
}
|
|
12
|
+
interface NarrowedUseAsyncDataI<StateT> {
|
|
13
|
+
<PathT extends null | string | undefined>(path: PathT, loader: AsyncDataLoaderT<DataInEnvelopeAtPathT<StateT, PathT>>, options?: UseAsyncDataOptionsT): UseAsyncDataResT<DataInEnvelopeAtPathT<StateT, PathT>>;
|
|
14
|
+
<Forced extends ForceT | false = false, DataT = unknown>(path: null | string | undefined, loader: AsyncDataLoaderT<TypeLock<Forced, void, DataT>>, options?: UseAsyncDataOptionsT): UseAsyncDataResT<TypeLock<Forced, never, DataT>>;
|
|
15
|
+
}
|
|
16
|
+
interface NarrowedUseAsyncCollectionI<StateT> {
|
|
17
|
+
<PathT extends null | string | undefined>(id: string, path: PathT, loader: AsyncCollectionLoaderT<DataInEnvelopeAtPathT<StateT, PathT>>, options?: UseAsyncDataOptionsT): UseAsyncDataResT<DataInEnvelopeAtPathT<StateT, PathT>>;
|
|
18
|
+
<Forced extends ForceT | false = false, DataT = unknown>(id: string, path: null | string | undefined, loader: AsyncCollectionLoaderT<TypeLock<Forced, void, DataT>>, options?: UseAsyncDataOptionsT): UseAsyncDataResT<DataT>;
|
|
19
|
+
}
|
|
20
|
+
type WithGlobalStateTypeResT<StateT, SsrContextT extends SsrContext<StateT> = SsrContext<StateT>> = {
|
|
21
|
+
getGlobalState: typeof getGlobalState<StateT, SsrContextT>;
|
|
22
|
+
getSsrContext: typeof getSsrContext<SsrContextT>;
|
|
23
|
+
GlobalStateProvider: typeof GlobalStateProvider<StateT, SsrContextT>;
|
|
24
|
+
useAsyncCollection: NarrowedUseAsyncCollectionI<StateT>;
|
|
25
|
+
useAsyncData: NarrowedUseAsyncDataI<StateT>;
|
|
26
|
+
useGlobalState: NarrowedUseGlobalStateI<StateT>;
|
|
27
|
+
};
|
|
28
|
+
export default function withGlobalStateType<StateT, SsrContextT extends SsrContext<StateT> = SsrContext<StateT>>(): WithGlobalStateTypeResT<StateT, SsrContextT>;
|
|
29
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dr.pogodin/react-global-state",
|
|
3
|
-
"version": "0.10.0-alpha.
|
|
3
|
+
"version": "0.10.0-alpha.3",
|
|
4
4
|
"description": "Hook-based global state for React",
|
|
5
|
-
"main": "./build/index.js",
|
|
5
|
+
"main": "./build/common/index.js",
|
|
6
6
|
"react-native": "src/index.ts",
|
|
7
7
|
"source": "src/index.ts",
|
|
8
|
+
"types": "./build/types/index.d.ts",
|
|
8
9
|
"exports": {
|
|
9
|
-
"
|
|
10
|
-
"
|
|
10
|
+
"module": "./build/module/index.js",
|
|
11
|
+
"node": "./build/common/index.js",
|
|
12
|
+
"default": "./build/common/index.js"
|
|
11
13
|
},
|
|
12
14
|
"scripts": {
|
|
13
|
-
"build": "rimraf build &&
|
|
15
|
+
"build": "rimraf build && npm run build:types && npm run build:common && npm run build:module",
|
|
16
|
+
"build:common": "rimraf build/common && babel src -x .ts,.tsx --out-dir build/common --source-maps",
|
|
17
|
+
"build:module": "rimraf build/module && babel src -x .ts,.tsx --out-dir build/module --source-maps --config-file ./babel.module.config.js",
|
|
18
|
+
"build:types": "rimraf build/types && tsc --project tsconfig.types.json",
|
|
14
19
|
"jest": "npm run jest:types && npm run jest:logic",
|
|
15
|
-
"jest:types": "jest --config jest/config-types.json",
|
|
16
20
|
"jest:logic": "jest --config jest/config.json -w 1",
|
|
21
|
+
"jest:types": "jest --config jest/config-types.json",
|
|
17
22
|
"lint": "eslint --ext .js,.jsx,.ts,.tsx .",
|
|
18
23
|
"test": "npm run lint && npm run typecheck && npm run jest",
|
|
19
|
-
"typecheck": "tsc --
|
|
24
|
+
"typecheck": "tsc --project __tests__/tsconfig.json"
|
|
20
25
|
},
|
|
21
26
|
"repository": "github:birdofpreyru/react-global-state",
|
|
22
27
|
"keywords": [
|
|
@@ -35,53 +40,53 @@
|
|
|
35
40
|
},
|
|
36
41
|
"homepage": "https://dr.pogodin.studio/docs/react-global-state/index.html",
|
|
37
42
|
"engines": {
|
|
38
|
-
"node": ">=
|
|
43
|
+
"node": ">=18"
|
|
39
44
|
},
|
|
40
45
|
"dependencies": {
|
|
41
|
-
"@babel/runtime": "^7.
|
|
46
|
+
"@babel/runtime": "^7.23.2",
|
|
42
47
|
"@dr.pogodin/js-utils": "^0.0.6",
|
|
43
48
|
"lodash": "^4.17.21",
|
|
44
|
-
"uuid": "^9.0.
|
|
49
|
+
"uuid": "^9.0.1"
|
|
45
50
|
},
|
|
46
51
|
"devDependencies": {
|
|
47
|
-
"@babel/cli": "^7.
|
|
48
|
-
"@babel/core": "^7.
|
|
49
|
-
"@babel/eslint-parser": "^7.22.
|
|
50
|
-
"@babel/eslint-plugin": "^7.22.
|
|
51
|
-
"@babel/node": "^7.22.
|
|
52
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
53
|
-
"@babel/preset-env": "^7.
|
|
54
|
-
"@babel/preset-react": "^7.22.
|
|
55
|
-
"@babel/preset-typescript": "^7.
|
|
56
|
-
"@tsconfig/recommended": "^1.0.
|
|
57
|
-
"@tsd/typescript": "^5.
|
|
58
|
-
"@types/jest": "^29.5.
|
|
59
|
-
"@types/lodash": "^4.14.
|
|
60
|
-
"@types/pretty": "^2.0.
|
|
61
|
-
"@types/react": "^18.2.
|
|
62
|
-
"@types/react-dom": "^18.2.
|
|
63
|
-
"@types/uuid": "^9.0.
|
|
64
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
65
|
-
"@typescript-eslint/parser": "^
|
|
66
|
-
"babel-jest": "^29.
|
|
52
|
+
"@babel/cli": "^7.23.0",
|
|
53
|
+
"@babel/core": "^7.23.2",
|
|
54
|
+
"@babel/eslint-parser": "^7.22.15",
|
|
55
|
+
"@babel/eslint-plugin": "^7.22.10",
|
|
56
|
+
"@babel/node": "^7.22.19",
|
|
57
|
+
"@babel/plugin-transform-runtime": "^7.23.2",
|
|
58
|
+
"@babel/preset-env": "^7.23.2",
|
|
59
|
+
"@babel/preset-react": "^7.22.15",
|
|
60
|
+
"@babel/preset-typescript": "^7.23.2",
|
|
61
|
+
"@tsconfig/recommended": "^1.0.3",
|
|
62
|
+
"@tsd/typescript": "^5.2.2",
|
|
63
|
+
"@types/jest": "^29.5.6",
|
|
64
|
+
"@types/lodash": "^4.14.200",
|
|
65
|
+
"@types/pretty": "^2.0.2",
|
|
66
|
+
"@types/react": "^18.2.31",
|
|
67
|
+
"@types/react-dom": "^18.2.14",
|
|
68
|
+
"@types/uuid": "^9.0.6",
|
|
69
|
+
"@typescript-eslint/eslint-plugin": "^6.8.0",
|
|
70
|
+
"@typescript-eslint/parser": "^6.8.0",
|
|
71
|
+
"babel-jest": "^29.7.0",
|
|
67
72
|
"babel-plugin-module-resolver": "^5.0.0",
|
|
68
|
-
"eslint": "^8.
|
|
73
|
+
"eslint": "^8.52.0",
|
|
69
74
|
"eslint-config-airbnb": "^19.0.4",
|
|
70
|
-
"eslint-config-airbnb-typescript": "^17.
|
|
75
|
+
"eslint-config-airbnb-typescript": "^17.1.0",
|
|
71
76
|
"eslint-import-resolver-babel-module": "^5.3.2",
|
|
72
|
-
"eslint-plugin-import": "^2.
|
|
73
|
-
"eslint-plugin-jest": "^27.
|
|
77
|
+
"eslint-plugin-import": "^2.28.1",
|
|
78
|
+
"eslint-plugin-jest": "^27.4.3",
|
|
74
79
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
|
75
|
-
"eslint-plugin-react": "^7.
|
|
80
|
+
"eslint-plugin-react": "^7.33.2",
|
|
76
81
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
77
|
-
"jest": "^29.
|
|
78
|
-
"jest-environment-jsdom": "^29.
|
|
82
|
+
"jest": "^29.7.0",
|
|
83
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
79
84
|
"jest-runner-tsd": "^6.0.0",
|
|
80
85
|
"mockdate": "^3.0.5",
|
|
81
86
|
"pretty": "^2.0.0",
|
|
82
87
|
"rimraf": "^5.0.1",
|
|
83
88
|
"tsd-lite": "^0.8.0",
|
|
84
|
-
"typescript": "^5.
|
|
89
|
+
"typescript": "^5.2.2"
|
|
85
90
|
},
|
|
86
91
|
"peerDependencies": {
|
|
87
92
|
"react": "^18.2.0",
|
package/src/GlobalState.ts
CHANGED
|
@@ -12,6 +12,7 @@ import SsrContext from './SsrContext';
|
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
14
|
type CallbackT,
|
|
15
|
+
type ForceT,
|
|
15
16
|
type TypeLock,
|
|
16
17
|
type ValueAtPathT,
|
|
17
18
|
type ValueOrInitializerT,
|
|
@@ -25,8 +26,11 @@ type GetOptsT<T> = {
|
|
|
25
26
|
initialValue?: ValueOrInitializerT<T>;
|
|
26
27
|
};
|
|
27
28
|
|
|
28
|
-
export default class GlobalState<
|
|
29
|
-
|
|
29
|
+
export default class GlobalState<
|
|
30
|
+
StateT,
|
|
31
|
+
SsrContextT extends SsrContext<StateT> = SsrContext<StateT>,
|
|
32
|
+
> {
|
|
33
|
+
readonly ssrContext?: SsrContextT;
|
|
30
34
|
|
|
31
35
|
#initialState: StateT;
|
|
32
36
|
|
|
@@ -47,7 +51,7 @@ export default class GlobalState<StateT> {
|
|
|
47
51
|
*/
|
|
48
52
|
constructor(
|
|
49
53
|
initialState: StateT,
|
|
50
|
-
ssrContext?:
|
|
54
|
+
ssrContext?: SsrContextT,
|
|
51
55
|
) {
|
|
52
56
|
this.#currentState = initialState;
|
|
53
57
|
this.#initialState = initialState;
|
|
@@ -158,10 +162,10 @@ export default class GlobalState<StateT> {
|
|
|
158
162
|
|
|
159
163
|
// This variant is not callable by default (without generic arguments),
|
|
160
164
|
// otherwise it allows to set the correct ValueT directly.
|
|
161
|
-
get<
|
|
165
|
+
get<Forced extends ForceT | false = false, ValueT = void>(
|
|
162
166
|
path?: null | string,
|
|
163
|
-
opts?: GetOptsT<TypeLock<
|
|
164
|
-
): TypeLock<
|
|
167
|
+
opts?: GetOptsT<TypeLock<Forced, never, ValueT>>,
|
|
168
|
+
): TypeLock<Forced, void, ValueT>;
|
|
165
169
|
|
|
166
170
|
get<ValueT>(path?: null | string, opts?: GetOptsT<ValueT>): ValueT {
|
|
167
171
|
if (isNil(path)) {
|
|
@@ -178,7 +182,7 @@ export default class GlobalState<StateT> {
|
|
|
178
182
|
res = isFunction(iv) ? iv() : iv;
|
|
179
183
|
|
|
180
184
|
if (!opts?.initialState || this.get(path) === undefined) {
|
|
181
|
-
this.set<
|
|
185
|
+
this.set<ForceT, unknown>(path, res);
|
|
182
186
|
}
|
|
183
187
|
|
|
184
188
|
return res;
|
|
@@ -201,10 +205,10 @@ export default class GlobalState<StateT> {
|
|
|
201
205
|
|
|
202
206
|
// This variant is disabled by default, otherwise allows to give
|
|
203
207
|
// expected value type explicitly.
|
|
204
|
-
set<
|
|
208
|
+
set<Forced extends ForceT | false = false, ValueT = never>(
|
|
205
209
|
path: null | string | undefined,
|
|
206
|
-
value: TypeLock<
|
|
207
|
-
): TypeLock<
|
|
210
|
+
value: TypeLock<Forced, never, ValueT>,
|
|
211
|
+
): TypeLock<Forced, void, ValueT>;
|
|
208
212
|
|
|
209
213
|
set(path: null | string | undefined, value: unknown): unknown {
|
|
210
214
|
if (isNil(path)) return this.setEntireState(value as StateT);
|
|
@@ -22,7 +22,10 @@ const context = createContext<GlobalState<unknown> | null>(null);
|
|
|
22
22
|
* the global state, instead of accessing it directly.
|
|
23
23
|
* @return
|
|
24
24
|
*/
|
|
25
|
-
export function getGlobalState<
|
|
25
|
+
export function getGlobalState<
|
|
26
|
+
StateT,
|
|
27
|
+
SsrContextT extends SsrContext<StateT> = SsrContext<StateT>,
|
|
28
|
+
>(): GlobalState<StateT, SsrContextT> {
|
|
26
29
|
// Here Rules of Hooks are violated because "getGlobalState()" does not follow
|
|
27
30
|
// convention that hook names should start with use... This is intentional in
|
|
28
31
|
// our case, as getGlobalState() hook is intended for advance scenarious,
|
|
@@ -32,7 +35,7 @@ export function getGlobalState<StateT>(): GlobalState<StateT> {
|
|
|
32
35
|
const globalState = useContext(context);
|
|
33
36
|
/* eslint-enable react-hooks/rules-of-hooks */
|
|
34
37
|
if (!globalState) throw new Error('Missing GlobalStateProvider');
|
|
35
|
-
return globalState as GlobalState<StateT>;
|
|
38
|
+
return globalState as GlobalState<StateT, SsrContextT>;
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
/**
|
|
@@ -49,25 +52,30 @@ export function getGlobalState<StateT>(): GlobalState<StateT> {
|
|
|
49
52
|
* - If `throwWithoutSsrContext` is `true`, and there is no SSR context attached
|
|
50
53
|
* to the global state provided by {@link <GlobalStateProvider>}.
|
|
51
54
|
*/
|
|
52
|
-
export function getSsrContext<
|
|
55
|
+
export function getSsrContext<
|
|
56
|
+
SsrContextT extends SsrContext<unknown>,
|
|
57
|
+
>(
|
|
53
58
|
throwWithoutSsrContext = true,
|
|
54
|
-
):
|
|
55
|
-
const { ssrContext } = getGlobalState<
|
|
59
|
+
): SsrContextT | undefined {
|
|
60
|
+
const { ssrContext } = getGlobalState<SsrContextT['state'], SsrContextT>();
|
|
56
61
|
if (!ssrContext && throwWithoutSsrContext) {
|
|
57
62
|
throw new Error('No SSR context found');
|
|
58
63
|
}
|
|
59
64
|
return ssrContext;
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
type NewStateProps<StateT
|
|
67
|
+
type NewStateProps<StateT, SsrContextT extends SsrContext<StateT>> = {
|
|
63
68
|
initialState: ValueOrInitializerT<StateT>,
|
|
64
|
-
ssrContext?:
|
|
69
|
+
ssrContext?: SsrContextT;
|
|
65
70
|
};
|
|
66
71
|
|
|
67
|
-
type GlobalStateProviderProps<
|
|
72
|
+
type GlobalStateProviderProps<
|
|
73
|
+
StateT,
|
|
74
|
+
SsrContextT extends SsrContext<StateT>,
|
|
75
|
+
> = {
|
|
68
76
|
children?: ReactNode;
|
|
69
|
-
} & (NewStateProps<StateT> | {
|
|
70
|
-
stateProxy: true | GlobalState<StateT>;
|
|
77
|
+
} & (NewStateProps<StateT, SsrContextT> | {
|
|
78
|
+
stateProxy: true | GlobalState<StateT, SsrContextT>;
|
|
71
79
|
});
|
|
72
80
|
|
|
73
81
|
/**
|
|
@@ -83,10 +91,13 @@ type GlobalStateProviderProps<StateT> = {
|
|
|
83
91
|
* - If `GlobalState` instance, it will be used by this provider.
|
|
84
92
|
* - If not given, a new `GlobalState` instance will be created and used.
|
|
85
93
|
*/
|
|
86
|
-
export default function GlobalStateProvider<
|
|
87
|
-
|
|
94
|
+
export default function GlobalStateProvider<
|
|
95
|
+
StateT,
|
|
96
|
+
SsrContextT extends SsrContext<StateT> = SsrContext<StateT>,
|
|
97
|
+
>(
|
|
98
|
+
{ children, ...rest }: GlobalStateProviderProps<StateT, SsrContextT>,
|
|
88
99
|
) {
|
|
89
|
-
const state = useRef<GlobalState<StateT>>();
|
|
100
|
+
const state = useRef<GlobalState<StateT, SsrContextT>>();
|
|
90
101
|
if (!state.current) {
|
|
91
102
|
// NOTE: The last part of condition, "&& rest.stateProxy", is needed for
|
|
92
103
|
// graceful compatibility with JavaScript - if "undefined" stateProxy value
|
|
@@ -96,9 +107,9 @@ export default function GlobalStateProvider<StateT>(
|
|
|
96
107
|
if (rest.stateProxy === true) state.current = getGlobalState();
|
|
97
108
|
else state.current = rest.stateProxy;
|
|
98
109
|
} else {
|
|
99
|
-
const { initialState, ssrContext } = rest as NewStateProps<StateT>;
|
|
110
|
+
const { initialState, ssrContext } = rest as NewStateProps<StateT, SsrContextT>;
|
|
100
111
|
|
|
101
|
-
state.current = new GlobalState<StateT>(
|
|
112
|
+
state.current = new GlobalState<StateT, SsrContextT>(
|
|
102
113
|
isFunction(initialState) ? initialState() : initialState,
|
|
103
114
|
ssrContext,
|
|
104
115
|
);
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,6 @@ export {
|
|
|
28
28
|
default as useGlobalState,
|
|
29
29
|
} from './useGlobalState';
|
|
30
30
|
|
|
31
|
-
export { type ValueOrInitializerT } from './utils';
|
|
31
|
+
export { type ForceT, type ValueOrInitializerT } from './utils';
|
|
32
32
|
|
|
33
33
|
export { default as withGlobalStateType } from './withGlobalStateType';
|
|
@@ -8,7 +8,7 @@ import useAsyncData, {
|
|
|
8
8
|
type UseAsyncDataResT,
|
|
9
9
|
} from './useAsyncData';
|
|
10
10
|
|
|
11
|
-
import { TypeLock } from './utils';
|
|
11
|
+
import { type ForceT, type TypeLock } from './utils';
|
|
12
12
|
|
|
13
13
|
export type AsyncCollectionLoaderT<DataT> =
|
|
14
14
|
(id: string, oldData: null | DataT) => DataT | Promise<DataT>;
|
|
@@ -70,14 +70,14 @@ function useAsyncCollection<
|
|
|
70
70
|
): UseAsyncDataResT<DataInEnvelopeAtPathT<StateT, `${PathT}.${IdT}`>>;
|
|
71
71
|
|
|
72
72
|
function useAsyncCollection<
|
|
73
|
-
|
|
73
|
+
Forced extends ForceT | false = false,
|
|
74
74
|
DataT = unknown,
|
|
75
75
|
>(
|
|
76
76
|
id: string,
|
|
77
77
|
path: null | string | undefined,
|
|
78
|
-
loader: AsyncCollectionLoaderT<TypeLock<
|
|
78
|
+
loader: AsyncCollectionLoaderT<TypeLock<Forced, void, DataT>>,
|
|
79
79
|
options?: UseAsyncDataOptionsT,
|
|
80
|
-
): UseAsyncDataResT<TypeLock<
|
|
80
|
+
): UseAsyncDataResT<TypeLock<Forced, void, DataT>>;
|
|
81
81
|
|
|
82
82
|
function useAsyncCollection<DataT>(
|
|
83
83
|
id: string,
|
|
@@ -86,7 +86,7 @@ function useAsyncCollection<DataT>(
|
|
|
86
86
|
options: UseAsyncDataOptionsT = {},
|
|
87
87
|
): UseAsyncDataResT<DataT> {
|
|
88
88
|
const itemPath = path ? `${path}.${id}` : id;
|
|
89
|
-
return useAsyncData<
|
|
89
|
+
return useAsyncData<ForceT, DataT>(
|
|
90
90
|
itemPath,
|
|
91
91
|
(oldData: null | DataT) => loader(id, oldData),
|
|
92
92
|
options,
|
package/src/useAsyncData.ts
CHANGED
|
@@ -10,11 +10,16 @@ import { MIN_MS } from '@dr.pogodin/js-utils';
|
|
|
10
10
|
|
|
11
11
|
import { getGlobalState } from './GlobalStateProvider';
|
|
12
12
|
import useGlobalState from './useGlobalState';
|
|
13
|
-
import { TypeLock, isDebugMode } from './utils';
|
|
14
13
|
|
|
15
|
-
import
|
|
14
|
+
import {
|
|
15
|
+
type ForceT,
|
|
16
|
+
type TypeLock,
|
|
17
|
+
type ValueAtPathT,
|
|
18
|
+
isDebugMode,
|
|
19
|
+
} from './utils';
|
|
16
20
|
|
|
17
|
-
import
|
|
21
|
+
import GlobalState from './GlobalState';
|
|
22
|
+
import SsrContext from './SsrContext';
|
|
18
23
|
|
|
19
24
|
const DEFAULT_MAXAGE = 5 * MIN_MS; // 5 minutes.
|
|
20
25
|
|
|
@@ -70,7 +75,7 @@ export type UseAsyncDataResT<DataT> = {
|
|
|
70
75
|
async function load<DataT>(
|
|
71
76
|
path: null | string | undefined,
|
|
72
77
|
loader: AsyncDataLoaderT<DataT>,
|
|
73
|
-
globalState: GlobalState<unknown
|
|
78
|
+
globalState: GlobalState<unknown, SsrContext<unknown>>,
|
|
74
79
|
oldData: DataT | null,
|
|
75
80
|
opIdPrefix: 'C' | 'S' = 'C',
|
|
76
81
|
): Promise<void> {
|
|
@@ -83,11 +88,11 @@ async function load<DataT>(
|
|
|
83
88
|
}
|
|
84
89
|
const operationId = opIdPrefix + uuid();
|
|
85
90
|
const operationIdPath = path ? `${path}.operationId` : 'operationId';
|
|
86
|
-
globalState.set<
|
|
91
|
+
globalState.set<ForceT, string>(operationIdPath, operationId);
|
|
87
92
|
const data = await loader(
|
|
88
|
-
oldData || (globalState.get<
|
|
93
|
+
oldData || (globalState.get<ForceT, AsyncDataEnvelopeT<DataT>>(path)).data,
|
|
89
94
|
);
|
|
90
|
-
const state: AsyncDataEnvelopeT<DataT> = globalState.get<
|
|
95
|
+
const state: AsyncDataEnvelopeT<DataT> = globalState.get<ForceT, AsyncDataEnvelopeT<DataT>>(path);
|
|
91
96
|
if (operationId === state.operationId) {
|
|
92
97
|
if (process.env.NODE_ENV !== 'production' && isDebugMode()) {
|
|
93
98
|
/* eslint-disable no-console */
|
|
@@ -99,7 +104,7 @@ async function load<DataT>(
|
|
|
99
104
|
console.log('Data:', cloneDeep(data));
|
|
100
105
|
/* eslint-enable no-console */
|
|
101
106
|
}
|
|
102
|
-
globalState.set<
|
|
107
|
+
globalState.set<ForceT, AsyncDataEnvelopeT<DataT>>(path, {
|
|
103
108
|
...state,
|
|
104
109
|
data,
|
|
105
110
|
operationId: '',
|
|
@@ -176,13 +181,13 @@ function useAsyncData<
|
|
|
176
181
|
): UseAsyncDataResT<DataInEnvelopeAtPathT<StateT, PathT>>;
|
|
177
182
|
|
|
178
183
|
function useAsyncData<
|
|
179
|
-
|
|
184
|
+
Forced extends ForceT | false = false,
|
|
180
185
|
DataT = unknown,
|
|
181
186
|
>(
|
|
182
187
|
path: null | string | undefined,
|
|
183
|
-
loader: AsyncDataLoaderT<TypeLock<
|
|
188
|
+
loader: AsyncDataLoaderT<TypeLock<Forced, void, DataT>>,
|
|
184
189
|
options?: UseAsyncDataOptionsT,
|
|
185
|
-
): UseAsyncDataResT<TypeLock<
|
|
190
|
+
): UseAsyncDataResT<TypeLock<Forced, void, DataT>>;
|
|
186
191
|
|
|
187
192
|
function useAsyncData<DataT>(
|
|
188
193
|
path: null | string | undefined,
|
|
@@ -201,7 +206,7 @@ function useAsyncData<DataT>(
|
|
|
201
206
|
// Note: here we can't depend on useGlobalState() to init the initial value,
|
|
202
207
|
// because that way we'll have issues with SSR (see details below).
|
|
203
208
|
const globalState = getGlobalState();
|
|
204
|
-
const state = globalState.get<
|
|
209
|
+
const state = globalState.get<ForceT, AsyncDataEnvelopeT<DataT>>(path, {
|
|
205
210
|
initialValue: newAsyncDataEnvelope<DataT>(),
|
|
206
211
|
});
|
|
207
212
|
|
|
@@ -223,10 +228,11 @@ function useAsyncData<DataT>(
|
|
|
223
228
|
// The same applies to other useEffect() hooks below.
|
|
224
229
|
useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks
|
|
225
230
|
const numRefsPath = path ? `${path}.numRefs` : 'numRefs';
|
|
226
|
-
const numRefs = globalState.get<
|
|
227
|
-
globalState.set<
|
|
231
|
+
const numRefs = globalState.get<ForceT, number>(numRefsPath);
|
|
232
|
+
globalState.set<ForceT, number>(numRefsPath, numRefs + 1);
|
|
228
233
|
return () => {
|
|
229
|
-
const state2: AsyncDataEnvelopeT<DataT> = globalState.get<
|
|
234
|
+
const state2: AsyncDataEnvelopeT<DataT> = globalState.get<
|
|
235
|
+
ForceT, AsyncDataEnvelopeT<DataT>>(
|
|
230
236
|
path,
|
|
231
237
|
);
|
|
232
238
|
if (
|
|
@@ -242,13 +248,13 @@ function useAsyncData<DataT>(
|
|
|
242
248
|
);
|
|
243
249
|
/* eslint-enable no-console */
|
|
244
250
|
}
|
|
245
|
-
globalState.set<
|
|
251
|
+
globalState.set<ForceT, AsyncDataEnvelopeT<DataT>>(path, {
|
|
246
252
|
...state2,
|
|
247
253
|
data: null,
|
|
248
254
|
numRefs: 0,
|
|
249
255
|
timestamp: 0,
|
|
250
256
|
});
|
|
251
|
-
} else globalState.set<
|
|
257
|
+
} else globalState.set<ForceT, number>(numRefsPath, state2.numRefs - 1);
|
|
252
258
|
};
|
|
253
259
|
}, [garbageCollectAge, globalState, path]);
|
|
254
260
|
|
|
@@ -258,7 +264,9 @@ function useAsyncData<DataT>(
|
|
|
258
264
|
// Data loading and refreshing.
|
|
259
265
|
let loadTriggered = false;
|
|
260
266
|
useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks
|
|
261
|
-
const state2: AsyncDataEnvelopeT<DataT> = globalState.get<
|
|
267
|
+
const state2: AsyncDataEnvelopeT<DataT> = globalState.get<
|
|
268
|
+
ForceT, AsyncDataEnvelopeT<DataT>>(path);
|
|
269
|
+
|
|
262
270
|
if (refreshAge < Date.now() - state2.timestamp
|
|
263
271
|
&& (!state2.operationId || state2.operationId.charAt(0) === 'S')) {
|
|
264
272
|
load(path, loader, globalState, state2.data);
|
|
@@ -272,7 +280,7 @@ function useAsyncData<DataT>(
|
|
|
272
280
|
}, deps); // eslint-disable-line react-hooks/exhaustive-deps
|
|
273
281
|
}
|
|
274
282
|
|
|
275
|
-
const [localState] = useGlobalState<
|
|
283
|
+
const [localState] = useGlobalState<ForceT, AsyncDataEnvelopeT<DataT>>(
|
|
276
284
|
path,
|
|
277
285
|
newAsyncDataEnvelope<DataT>(),
|
|
278
286
|
);
|
package/src/useGlobalState.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { getGlobalState } from './GlobalStateProvider';
|
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
12
|
type CallbackT,
|
|
13
|
+
type ForceT,
|
|
13
14
|
type TypeLock,
|
|
14
15
|
type ValueAtPathT,
|
|
15
16
|
type ValueOrInitializerT,
|
|
@@ -82,6 +83,11 @@ export type UseGlobalStateResT<T> = [T, SetterT<T>];
|
|
|
82
83
|
|
|
83
84
|
function useGlobalState<StateT>(): UseGlobalStateResT<StateT>;
|
|
84
85
|
|
|
86
|
+
function useGlobalState<Forced extends ForceT | false = false, ValueT = unknown>(
|
|
87
|
+
path: null | string | undefined,
|
|
88
|
+
initialValue?: ValueOrInitializerT<TypeLock<Forced, never, ValueT>>,
|
|
89
|
+
): UseGlobalStateResT<TypeLock<Forced, void, ValueT>>;
|
|
90
|
+
|
|
85
91
|
function useGlobalState<
|
|
86
92
|
StateT,
|
|
87
93
|
PathT extends null | string | undefined,
|
|
@@ -90,14 +96,6 @@ function useGlobalState<
|
|
|
90
96
|
initialValue?: ValueOrInitializerT<ValueAtPathT<StateT, PathT, never>>
|
|
91
97
|
): UseGlobalStateResT<ValueAtPathT<StateT, PathT, void>>;
|
|
92
98
|
|
|
93
|
-
function useGlobalState<
|
|
94
|
-
Unlocked extends 0 | 1 = 0,
|
|
95
|
-
ValueT = void,
|
|
96
|
-
>(
|
|
97
|
-
path: null | string | undefined,
|
|
98
|
-
initialValue?: ValueOrInitializerT<TypeLock<Unlocked, never, ValueT>>,
|
|
99
|
-
): UseGlobalStateResT<TypeLock<Unlocked, void, ValueT>>;
|
|
100
|
-
|
|
101
99
|
function useGlobalState(
|
|
102
100
|
path?: null | string,
|
|
103
101
|
initialValue?: ValueOrInitializerT<unknown>,
|
|
@@ -122,7 +120,7 @@ function useGlobalState(
|
|
|
122
120
|
console.groupEnd();
|
|
123
121
|
/* eslint-enable no-console */
|
|
124
122
|
}
|
|
125
|
-
rc.globalState.set<
|
|
123
|
+
rc.globalState.set<ForceT, unknown>(rc.path, newState);
|
|
126
124
|
},
|
|
127
125
|
state: isFunction(initialValue) ? initialValue() : initialValue,
|
|
128
126
|
watcher: () => {
|
|
@@ -137,8 +135,8 @@ function useGlobalState(
|
|
|
137
135
|
|
|
138
136
|
rc.state = useSyncExternalStore(
|
|
139
137
|
(cb) => rc.emitter.addListener(cb),
|
|
140
|
-
() => rc.globalState.get<
|
|
141
|
-
() => rc.globalState.get<
|
|
138
|
+
() => rc.globalState.get<ForceT, unknown>(rc.path, { initialValue }),
|
|
139
|
+
() => rc.globalState.get<ForceT, unknown>(rc.path, { initialValue, initialState: true }),
|
|
142
140
|
);
|
|
143
141
|
|
|
144
142
|
useEffect(() => {
|
package/src/utils.ts
CHANGED
|
@@ -2,12 +2,17 @@ import { type GetFieldType } from 'lodash';
|
|
|
2
2
|
|
|
3
3
|
export type CallbackT = () => void;
|
|
4
4
|
|
|
5
|
-
// TODO: This probably should be moved to JS Utils lib.
|
|
5
|
+
// TODO: This (ForceT & TypeLock) probably should be moved to JS Utils lib.
|
|
6
|
+
|
|
7
|
+
// This type is used to "unlocked" special generic overrides around the library.
|
|
8
|
+
declare const force: unique symbol;
|
|
9
|
+
export type ForceT = typeof force;
|
|
10
|
+
|
|
6
11
|
export type TypeLock<
|
|
7
|
-
Unlocked extends
|
|
12
|
+
Unlocked extends ForceT | false,
|
|
8
13
|
LockedT extends never | void,
|
|
9
14
|
UnlockedT,
|
|
10
|
-
> = Unlocked extends
|
|
15
|
+
> = Unlocked extends ForceT ? UnlockedT : LockedT;
|
|
11
16
|
|
|
12
17
|
/**
|
|
13
18
|
* Given the type of state, `StateT`, and the type of state path, `PathT`,
|