@dr.pogodin/react-global-state 0.6.3 → 0.6.6
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 +77 -0
- package/build/node/GlobalState.js +5 -15
- package/build/node/GlobalState.js.map +1 -1
- package/build/node/GlobalStateProvider.js +2 -22
- package/build/node/GlobalStateProvider.js.map +1 -1
- package/build/node/useAsyncCollection.js +1 -14
- package/build/node/useAsyncCollection.js.map +1 -1
- package/build/node/useAsyncData.js +1 -34
- package/build/node/useAsyncData.js.map +1 -1
- package/build/node/useGlobalState.js +1 -3
- package/build/node/useGlobalState.js.map +1 -1
- package/build/node/utils.js +0 -12
- package/build/node/utils.js.map +1 -1
- package/build/web/GlobalState.js +7 -17
- package/build/web/GlobalState.js.map +1 -1
- package/build/web/GlobalStateProvider.js +2 -22
- package/build/web/GlobalStateProvider.js.map +1 -1
- package/build/web/useAsyncCollection.js +1 -14
- package/build/web/useAsyncCollection.js.map +1 -1
- package/build/web/useAsyncData.js +3 -36
- package/build/web/useAsyncData.js.map +1 -1
- package/build/web/useGlobalState.js +1 -3
- package/build/web/useGlobalState.js.map +1 -1
- package/build/web/utils.js +0 -12
- package/build/web/utils.js.map +1 -1
- package/package.json +20 -22
package/README.md
CHANGED
|
@@ -257,5 +257,82 @@ of some, or all async data at the server side.
|
|
|
257
257
|
}
|
|
258
258
|
```
|
|
259
259
|
|
|
260
|
+
### Frequently Asked Questions
|
|
261
|
+
|
|
262
|
+
- _Does React Global State library avoid unnecessary component re-renders when values updated in the global state are irrelevant to those components?_
|
|
263
|
+
|
|
264
|
+
Yes, it does avoid unnecessary re-renders of the component tree. A component
|
|
265
|
+
relying on `some.path` in the global state is re-rendered only when the value
|
|
266
|
+
at this path, or its sub-path has changed; _i.e._ it will be re-rendered if
|
|
267
|
+
the value at `some.path` has changed, and it will be re-rendered if the value
|
|
268
|
+
at `some.path.sub.path` has changed.
|
|
269
|
+
|
|
270
|
+
- _How would you describe your use case compared to another React global state library, e.g. [Valtio](https://www.npmjs.com/package/valtio)?_
|
|
271
|
+
|
|
272
|
+
1. React Global State is designed to follow the standard React API as close
|
|
273
|
+
as possible. _E.g._ if some component relies on the local state:
|
|
274
|
+
```jsx
|
|
275
|
+
const [value, setValue] = useState(initialState);
|
|
276
|
+
```
|
|
277
|
+
to move that value to the global state (or _vice versa_) one only needs to
|
|
278
|
+
replace the hook with
|
|
279
|
+
```jsx
|
|
280
|
+
const [value, setValue] = useGlobalState(path, initialState);
|
|
281
|
+
```
|
|
282
|
+
The [useGlobalState()] hook takes care to follow all edge cases of the
|
|
283
|
+
standard [useState()]: `setValue` setter identity is stable (does not
|
|
284
|
+
change on re-renders), functional updates and lazy initial state are
|
|
285
|
+
supported.
|
|
286
|
+
|
|
287
|
+
Other libraries tend to re-invent the wheel, introducing their own APIs,
|
|
288
|
+
which (i) should be learned and understood; (ii) do complicate migration
|
|
289
|
+
of components between the local and global state, should it be needed in
|
|
290
|
+
a course of app development / prototyping.
|
|
291
|
+
|
|
292
|
+
2. When it comes to async data in the global state other libraries tend to
|
|
293
|
+
offer only a very basic supported, often relying on experimental or internal
|
|
294
|
+
React mechanics.
|
|
295
|
+
|
|
296
|
+
React Global State, [useAsyncData()] and [useAsyncCollection()] hooks in
|
|
297
|
+
particular, implements async data fetching and management features: when
|
|
298
|
+
multiple components use these hooks to load async data to the same global
|
|
299
|
+
state path the library takes care to do the actual loading just once, and
|
|
300
|
+
then keep the data without reloading until their age reaches (configurable)
|
|
301
|
+
max age. There is an automated garbage collection of expired, non-used
|
|
302
|
+
async data from the global state; there is server-side rendering (SSR)
|
|
303
|
+
support, with suggested high-level setup taking care that all async data
|
|
304
|
+
loaded using [useAsyncData()] and [useAsyncCollection()] hooks will be
|
|
305
|
+
automatically loaded and used in server-side renders (still allowing to
|
|
306
|
+
opt-out of that for individual hooks, and timeout server-side fetching of
|
|
307
|
+
data that take too long to arrive, in which case the library will fetch
|
|
308
|
+
such data client-side). It does not rely on experimental React APIs to
|
|
309
|
+
achieve its functionality, it only uses current public APIs.
|
|
310
|
+
|
|
311
|
+
For me the support of async data fetching into the global state and their
|
|
312
|
+
further management with out-of-the-box SSR support was the primary
|
|
313
|
+
motivation to create React Global State. There are many other global state
|
|
314
|
+
React libraries, but I was not able to find any that would cover the async
|
|
315
|
+
data handling with that ease I believed was possible. The secondary
|
|
316
|
+
motivation was that existing global state libraries either had
|
|
317
|
+
the shortcoming of unnecessary component re-renders when data irrelevant
|
|
318
|
+
to them where updated in the global state, or introduced their
|
|
319
|
+
own APIs, where following the standard React APIs for local state looks
|
|
320
|
+
to me a way more convenient approach.
|
|
321
|
+
|
|
322
|
+
- _Is React Global State library production ready (considering the current version number 0.y.z)?_
|
|
323
|
+
|
|
324
|
+
Yes. I personally use it in production for all my commercial and personal
|
|
325
|
+
React projects for over an year. I just don't feel like to call it v1 until
|
|
326
|
+
a reasonable adoption by 3rd party developers, and any API improvements that
|
|
327
|
+
may come out of community experience.
|
|
328
|
+
|
|
260
329
|
[Library Reference](https://dr.pogodin.studio/docs/react-global-state/index.html) •
|
|
261
330
|
[Blog Article](https://dr.pogodin.studio/dev-blog/the-global-state-in-react-designed-right)
|
|
331
|
+
|
|
332
|
+
[useAsyncCollection()]: https://dr.pogodin.studio/docs/react-global-state/docs/api/hooks/useasynccollection
|
|
333
|
+
[useAsyncData()]: https://dr.pogodin.studio/docs/react-global-state/docs/api/hooks/useasyncdata
|
|
334
|
+
[useGlobalState()]: https://dr.pogodin.studio/docs/react-global-state/docs/api/hooks/useglobalstate
|
|
335
|
+
[useState()]: https://reactjs.org/docs/hooks-reference.html#usestate
|
|
336
|
+
|
|
337
|
+
[functional updates]: https://reactjs.org/docs/hooks-reference.html#functional-updates
|
|
338
|
+
[lazy initial state]: https://reactjs.org/docs/hooks-reference.html#functional-updates
|
|
@@ -23,9 +23,7 @@ function fullPath(statePath) {
|
|
|
23
23
|
|
|
24
24
|
class GlobalState {
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
* @classdesc Represents global state objects.
|
|
28
|
-
* @desc Creates a new global state object.
|
|
26
|
+
* Creates a new global state object.
|
|
29
27
|
* @param {any} [initialState] Intial global state content.
|
|
30
28
|
* @param {SsrContext} [ssrContext] Server-side rendering context.
|
|
31
29
|
*/
|
|
@@ -55,9 +53,7 @@ class GlobalState {
|
|
|
55
53
|
|
|
56
54
|
}
|
|
57
55
|
/**
|
|
58
|
-
*
|
|
59
|
-
* @memberof GlobalState
|
|
60
|
-
* @desc Gets the value at given `path` of global state. If `path` is null or
|
|
56
|
+
* Gets the value at given `path` of global state. If `path` is null or
|
|
61
57
|
* undefined, the entire state object is returned.
|
|
62
58
|
* @param {string} [path] Dot-delimitered state path. If not given, entire
|
|
63
59
|
* global state content is returned.
|
|
@@ -69,9 +65,7 @@ class GlobalState {
|
|
|
69
65
|
return (0, _lodash.get)(this, fullPath(path));
|
|
70
66
|
}
|
|
71
67
|
/**
|
|
72
|
-
*
|
|
73
|
-
* @memberof GlobalState
|
|
74
|
-
* @desc Writes the `value` to given global state `path`.
|
|
68
|
+
* Writes the `value` to given global state `path`.
|
|
75
69
|
* @param {string} [path] Dot-delimitered state path. If not given, entire
|
|
76
70
|
* global state content is replaced by the `value`.
|
|
77
71
|
* @param {any} value The value.
|
|
@@ -129,9 +123,7 @@ class GlobalState {
|
|
|
129
123
|
return value;
|
|
130
124
|
}
|
|
131
125
|
/**
|
|
132
|
-
*
|
|
133
|
-
* @memberof GlobalState
|
|
134
|
-
* @desc Unsubscribes `callback` from watching state updates; no operation if
|
|
126
|
+
* Unsubscribes `callback` from watching state updates; no operation if
|
|
135
127
|
* `callback` is not subscribed to the state updates.
|
|
136
128
|
* @param {function} callback
|
|
137
129
|
* @throws if {@link SsrContext} is attached to the state instance: the state
|
|
@@ -152,9 +144,7 @@ class GlobalState {
|
|
|
152
144
|
}
|
|
153
145
|
}
|
|
154
146
|
/**
|
|
155
|
-
*
|
|
156
|
-
* @memberof GlobalState
|
|
157
|
-
* @desc Subscribes `callback` to watch state updates; no operation if
|
|
147
|
+
* Subscribes `callback` to watch state updates; no operation if
|
|
158
148
|
* `callback` is already subscribed to this state instance.
|
|
159
149
|
* @param {function} callback It will be called without any arguments every
|
|
160
150
|
* time the state content changes (note, howhever, separate state updates can
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/GlobalState.js"],"names":["ERR_NO_SSR_WATCH","fullPath","statePath","GlobalState","constructor","initialState","ssrContext","state","nextNotifierId","watchers","dirty","pending","process","env","NODE_ENV","msg","console","groupCollapsed","log","groupEnd","get","path","set","value","p","pos","pathSegments","i","length","seg","next","setTimeout","forEach","w","unWatch","callback","Error","indexOf","pop","watch","push"],"mappings":";;;;;;;AAAA;;AAUA;;AAEA,MAAMA,gBAAgB,GAAG,gDAAzB;AAEA;AACA;AACA;AACA;AACA;AACA;;AACA,SAASC,QAAT,CAAkBC,SAAlB,EAA6B;AAC3B,SAAO,mBAAMA,SAAN,IAAmB,OAAnB,GAA8B,SAAQA,SAAU,EAAvD;AACD;;AAEc,MAAMC,WAAN,CAAkB;AAC/B;AACF;AACA;AACA;AACA;
|
|
1
|
+
{"version":3,"sources":["../../src/GlobalState.js"],"names":["ERR_NO_SSR_WATCH","fullPath","statePath","GlobalState","constructor","initialState","ssrContext","state","nextNotifierId","watchers","dirty","pending","process","env","NODE_ENV","msg","console","groupCollapsed","log","groupEnd","get","path","set","value","p","pos","pathSegments","i","length","seg","next","setTimeout","forEach","w","unWatch","callback","Error","indexOf","pop","watch","push"],"mappings":"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","sourcesContent":["import {\n cloneDeep,\n get,\n isArray,\n isObject,\n isNil,\n set,\n toPath,\n} from 'lodash';\n\nimport { isDebugMode } from './utils';\n\nconst ERR_NO_SSR_WATCH = 'GlobalState must not be watched at server side';\n\n/**\n * Transform state path into the full path inside GlobalState object.\n * @param {string} statePath\n * @return {string}\n * @ignore\n */\nfunction fullPath(statePath) {\n return isNil(statePath) ? 'state' : `state.${statePath}`;\n}\n\nexport default class GlobalState {\n /**\n * Creates a new global state object.\n * @param {any} [initialState] Intial global state content.\n * @param {SsrContext} [ssrContext] Server-side rendering context.\n */\n constructor(initialState, ssrContext) {\n /* eslint-disable no-param-reassign */\n this.state = cloneDeep(initialState);\n this.nextNotifierId = null;\n this.watchers = [];\n\n if (ssrContext) {\n ssrContext.dirty = false;\n ssrContext.pending = [];\n ssrContext.state = this.state;\n this.ssrContext = ssrContext;\n }\n\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n let msg = 'New ReactGlobalState created';\n if (ssrContext) msg += ' (SSR mode)';\n console.groupCollapsed(msg);\n console.log('Initial state:', cloneDeep(initialState));\n console.groupEnd();\n /* eslint-enable no-console */\n }\n /* eslint-enable no-param-reassign */\n }\n\n /**\n * Gets the value at given `path` of global state. If `path` is null or\n * undefined, the entire state object is returned.\n * @param {string} [path] Dot-delimitered state path. If not given, entire\n * global state content is returned.\n * @return {any}\n */\n get(path) {\n return get(this, fullPath(path));\n }\n\n /**\n * Writes the `value` to given global state `path`.\n * @param {string} [path] Dot-delimitered state path. If not given, entire\n * global state content is replaced by the `value`.\n * @param {any} value The value.\n * @return {any} Given `value` itself.\n */\n set(path, value) {\n const p = fullPath(path);\n if (value !== get(this, p)) {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupCollapsed(\n `ReactGlobalState update. Path: \"${path || ''}\"`,\n );\n console.log('New value:', cloneDeep(value));\n /* eslint-enable no-console */\n }\n let pos = this;\n const pathSegments = toPath(p);\n for (let i = 0; i < pathSegments.length - 1; i += 1) {\n const seg = pathSegments[i];\n const next = pos[seg];\n if (isArray(next)) pos[seg] = [...next];\n else if (isObject(next)) pos[seg] = { ...next };\n else break;\n pos = pos[seg];\n }\n\n // TODO: With such naive use of _.set, the state is mutated in place,\n // which may cause tons of unexpected side effects for dependants.\n // It will be better to partially clone the state, so that any existing\n // references are not mutated, while the full deep clonning is also\n // avoided.\n set(this, p, value);\n\n if (this.ssrContext) {\n this.ssrContext.dirty = true;\n this.ssrContext.state = this.state;\n } else if (!this.nextNotifierId) {\n this.nextNotifierId = setTimeout(() => {\n this.nextNotifierId = null;\n [...this.watchers].forEach((w) => w());\n });\n }\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.log('New state:', cloneDeep(this.state));\n console.groupEnd();\n /* eslint-enable no-console */\n }\n }\n return value;\n }\n\n /**\n * Unsubscribes `callback` from watching state updates; no operation if\n * `callback` is not subscribed to the state updates.\n * @param {function} callback\n * @throws if {@link SsrContext} is attached to the state instance: the state\n * watching functionality is intended for client-side (non-SSR) only.\n */\n unWatch(callback) {\n if (this.ssrContext) throw new Error(ERR_NO_SSR_WATCH);\n const { watchers } = this;\n const pos = watchers.indexOf(callback);\n if (pos >= 0) {\n watchers[pos] = watchers[watchers.length - 1];\n watchers.pop();\n }\n }\n\n /**\n * Subscribes `callback` to watch state updates; no operation if\n * `callback` is already subscribed to this state instance.\n * @param {function} callback It will be called without any arguments every\n * time the state content changes (note, howhever, separate state updates can\n * be applied to the state at once, and watching callbacks will be called once\n * after such bulk update).\n * @throws if {@link SsrContext} is attached to the state instance: the state\n * watching functionality is intended for client-side (non-SSR) only.\n */\n watch(callback) {\n if (this.ssrContext) throw new Error(ERR_NO_SSR_WATCH);\n const { watchers } = this;\n if (watchers.indexOf(callback) < 0) {\n watchers.push(callback);\n }\n }\n}\n"],"file":"GlobalState.js"}
|
|
@@ -16,27 +16,9 @@ var _GlobalState = _interopRequireDefault(require("./GlobalState"));
|
|
|
16
16
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
17
|
|
|
18
18
|
/* eslint-disable react/prop-types */
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @typedef {object} SsrContext Holds global-state-related information,
|
|
22
|
-
* which should be persistent across rendering iterations during server-side
|
|
23
|
-
* rendering (SSR). For the first SSR iteration any object, including an empty
|
|
24
|
-
* `{}`, may be provided to {@link <GlobalStateProvider>}: in either case
|
|
25
|
-
* all its fields listed below will be (re-)initialized as needed, and any other
|
|
26
|
-
* fields contained in the object won't be touched by the library (thus, you may
|
|
27
|
-
* use it to keep other data you need across SSR iterations, and you can access
|
|
28
|
-
* it from React components via {@link getSsrContext} hook).
|
|
29
|
-
* @prop {boolean} dirty `true` if the global state has been modified in
|
|
30
|
-
* the last SSR iteration; `false` otherwise.
|
|
31
|
-
* @prop {Promise[]} pending An array of promises waiting for completion of
|
|
32
|
-
* asynchronous state operations (like {@link useAsyncData}), initiated during
|
|
33
|
-
* the last SSR iteration.
|
|
34
|
-
* @prop {any} state The global state content at the end of last SSR iteration.
|
|
35
|
-
*/
|
|
36
19
|
const context = /*#__PURE__*/(0, _react.createContext)();
|
|
37
20
|
/**
|
|
38
|
-
* @
|
|
39
|
-
* @desc Gets {@link GlobalState} instance from the context. In most cases
|
|
21
|
+
* Gets {@link GlobalState} instance from the context. In most cases
|
|
40
22
|
* you should use {@link useGlobalState}, and other hooks to interact with
|
|
41
23
|
* the global state, instead of accessing it directly.
|
|
42
24
|
* @return {GlobalState}
|
|
@@ -84,9 +66,7 @@ function getSsrContext(throwWithoutSsrContext = true) {
|
|
|
84
66
|
return ssrContext;
|
|
85
67
|
}
|
|
86
68
|
/**
|
|
87
|
-
*
|
|
88
|
-
* @name <GlobalStateProvider>
|
|
89
|
-
* @desc Provides global state to its children.
|
|
69
|
+
* Provides global state to its children.
|
|
90
70
|
* @prop {ReactNode} [children] Component children, which will be provided with
|
|
91
71
|
* the global state, and rendered in place of the provider.
|
|
92
72
|
* @prop {any} [initialState] Initial content of the global state.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/GlobalStateProvider.jsx"],"names":["context","getGlobalState","globalState","Error","getSsrContext","throwWithoutSsrContext","ssrContext","GlobalStateProvider","children","initialState","stateProxy","state","GlobalState"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../../src/GlobalStateProvider.jsx"],"names":["context","getGlobalState","globalState","Error","getSsrContext","throwWithoutSsrContext","ssrContext","GlobalStateProvider","children","initialState","stateProxy","state","GlobalState"],"mappings":";;;;;;;;;;;AAEA;;AAEA;;;;AAJA;AAMA,MAAMA,OAAO,gBAAG,2BAAhB;AAEA;AACA;AACA;AACA;AACA;AACA;;AACO,SAASC,cAAT,GAA0B;AAC/B;AACA;AACA;AACA;AACA;;AACA;AACA,QAAMC,WAAW,GAAG,uBAAWF,OAAX,CAApB;AACA;;AACA,MAAI,CAACE,WAAL,EAAkB,MAAM,IAAIC,KAAJ,CAAU,6BAAV,CAAN;AAClB,SAAOD,WAAP;AACD;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACO,SAASE,aAAT,CAAuBC,sBAAsB,GAAG,IAAhD,EAAsD;AAC3D,QAAM;AAAEC,IAAAA;AAAF,MAAiBL,cAAc,EAArC;;AACA,MAAI,CAACK,UAAD,IAAeD,sBAAnB,EAA2C;AACzC,UAAM,IAAIF,KAAJ,CAAU,sBAAV,CAAN;AACD;;AACD,SAAOG,UAAP;AACD;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACe,SAASC,mBAAT,CAA6B;AAC1CC,EAAAA,QAD0C;AAE1CC,EAAAA,YAF0C;AAG1CH,EAAAA,UAH0C;AAI1CI,EAAAA;AAJ0C,CAA7B,EAKZ;AACD,MAAIC,KAAJ,CADC,CAED;AACA;AACA;AACA;;AACA;;AACA,MAAID,UAAU,YAAYE,oBAA1B,EAAuCD,KAAK,GAAGD,UAAR,CAAvC,KACK,IAAIA,UAAJ,EAAgBC,KAAK,GAAGV,cAAc,EAAtB,CAAhB,KACA,CAACU,KAAD,IAAU,qBAAS,IAAIC,oBAAJ,CAAgBH,YAAhB,EAA8BH,UAA9B,CAAT,CAAV;AACL;;AACA,sBACE,qBAAC,OAAD,CAAS,QAAT;AAAkB,IAAA,KAAK,EAAEK,KAAzB;AAAA,cACGH;AADH,IADF;AAKD","sourcesContent":["/* eslint-disable react/prop-types */\n\nimport { createContext, useContext, useState } from 'react';\n\nimport GlobalState from './GlobalState';\n\nconst context = createContext();\n\n/**\n * Gets {@link GlobalState} instance from the context. In most cases\n * you should use {@link useGlobalState}, and other hooks to interact with\n * the global state, instead of accessing it directly.\n * @return {GlobalState}\n */\nexport function getGlobalState() {\n // Here Rules of Hooks are violated because \"getGlobalState()\" does not follow\n // convention that hook names should start with use... This is intentional in\n // our case, as getGlobalState() hook is intended for advance scenarious,\n // while the normal interaction with the global state should happen via\n // another hook, useGlobalState().\n /* eslint-disable react-hooks/rules-of-hooks */\n const globalState = useContext(context);\n /* eslint-enable react-hooks/rules-of-hooks */\n if (!globalState) throw new Error('Missing GlobalStateProvider');\n return globalState;\n}\n\n/**\n * @category Hooks\n * @desc Gets SSR context.\n * @param {boolean} [throwWithoutSsrContext=true] If `true` (default),\n * this hook will throw if no SSR context is attached to the global state;\n * set `false` to not throw in such case. In either case the hook will throw\n * if the {@link <GlobalStateProvider>} (hence the state) is missing.\n * @returns {SsrContext} SSR context.\n * @throws\n * - If current component has no parent {@link <GlobalStateProvider>}\n * in the rendered React tree.\n * - If `throwWithoutSsrContext` is `true`, and there is no SSR context attached\n * to the global state provided by {@link <GlobalStateProvider>}.\n */\nexport function getSsrContext(throwWithoutSsrContext = true) {\n const { ssrContext } = getGlobalState();\n if (!ssrContext && throwWithoutSsrContext) {\n throw new Error('No SSR context found');\n }\n return ssrContext;\n}\n\n/**\n * Provides global state to its children.\n * @prop {ReactNode} [children] Component children, which will be provided with\n * the global state, and rendered in place of the provider.\n * @prop {any} [initialState] Initial content of the global state.\n * @prop {SsrContext} [ssrContext] Server-side rendering (SSR) context.\n * @prop {boolean|GlobalState} [stateProxy] This option is useful for code\n * splitting and SSR implementation:\n * - If `true`, this provider instance will fetch and reuse the global state\n * from a parent provider.\n * - If `GlobalState` instance, it will be used by this provider.\n * - If not given, a new `GlobalState` instance will be created and used.\n */\nexport default function GlobalStateProvider({\n children,\n initialState,\n ssrContext,\n stateProxy,\n}) {\n let state;\n // Here Rules of Hooks are violated because hooks are called conditionally,\n // however we assume that these properties should not change at runtime, thus\n // the actual hook order is preserved. Probably, it should be handled better,\n // though.\n /* eslint-disable react-hooks/rules-of-hooks */\n if (stateProxy instanceof GlobalState) state = stateProxy;\n else if (stateProxy) state = getGlobalState();\n else [state] = useState(new GlobalState(initialState, ssrContext));\n /* eslint-enable react-hooks/rules-of-hooks */\n return (\n <context.Provider value={state}>\n {children}\n </context.Provider>\n );\n}\n"],"file":"GlobalStateProvider.js"}
|
|
@@ -14,20 +14,7 @@ var _useAsyncData = _interopRequireDefault(require("./useAsyncData"));
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
* `loader` function accepted by {@link useAsyncCollection} hook.
|
|
19
|
-
* @param {string} id ID of the collection item to load.
|
|
20
|
-
* @param {any} oldData Previously fetched data for this ID, if any. The loader
|
|
21
|
-
* does not have to use it, it is provided just for convenience, when the newly
|
|
22
|
-
* resolved data may depend on the previously fetched data.
|
|
23
|
-
* @returns {Promise<any>} Resolves to data to be stored in the global state
|
|
24
|
-
* for the given collection item ID.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @category Hooks
|
|
29
|
-
* @func useAsyncCollection
|
|
30
|
-
* @desc Resolves and stores at the given `path` of global state elements of
|
|
17
|
+
* Resolves and stores at the given `path` of global state elements of
|
|
31
18
|
* an asynchronous data collection. In other words, it is an auxiliar wrapper
|
|
32
19
|
* around {@link useAsyncData}, which uses a loader which resolves to different
|
|
33
20
|
* data, based on ID argument passed in, and stores data fetched for different
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/useAsyncCollection.js"],"names":["useAsyncCollection","id","path","loader","options","itemPath","oldData"],"mappings":";;;;;;;;;AAIA;;AAJA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA
|
|
1
|
+
{"version":3,"sources":["../../src/useAsyncCollection.js"],"names":["useAsyncCollection","id","path","loader","options","itemPath","oldData"],"mappings":";;;;;;;;;AAIA;;AAJA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAASA,kBAAT,CACbC,EADa,EAEbC,IAFa,EAGbC,MAHa,EAIbC,OAAO,GAAG,EAJG,EAKb;AACA,QAAMC,QAAQ,GAAGH,IAAI,GAAI,GAAEA,IAAK,IAAGD,EAAG,EAAjB,GAAqBA,EAA1C;AACA,SAAO,2BAAaI,QAAb,EAAwBC,OAAD,IAAaH,MAAM,CAACF,EAAD,EAAKK,OAAL,CAA1C,EAAyDF,OAAzD,CAAP;AACD","sourcesContent":["/**\n * Loads and uses an item in an async collection.\n */\n\nimport useAsyncData from './useAsyncData';\n\n/**\n * Resolves and stores at the given `path` of global state elements of\n * an asynchronous data collection. In other words, it is an auxiliar wrapper\n * around {@link useAsyncData}, which uses a loader which resolves to different\n * data, based on ID argument passed in, and stores data fetched for different\n * IDs in the state.\n * @param {string} id ID of the collection item to load & use.\n * @param {string} path The global state path where entire collection should be\n * stored.\n * @param {AsyncCollectionLoader} loader A loader function, which takes an\n * ID of data to load, and resolves to the corresponding data.\n * @param {object} [options] Additional options.\n * @param {any[]} [options.deps=[]] An array of dependencies, which trigger\n * data reload when changed. Given dependency changes are watched shallowly\n * (similarly to the standard React's\n * [useEffect()](https://reactjs.org/docs/hooks-reference.html#useeffect)).\n * @param {boolean} [options.noSSR] If `true`, this hook won't load data during\n * server-side rendering.\n * @param {number} [options.garbageCollectAge=maxage] The maximum age of data\n * (in milliseconds), after which they are dropped from the state when the last\n * component referencing them via `useAsyncData()` hook unmounts. Defaults to\n * `maxage` option value.\n * @param {number} [options.maxage=5 x 60 x 1000] The maximum age of\n * data (in milliseconds) acceptable to the hook's caller. If loaded data are\n * older than this value, `null` is returned instead. Defaults to 5 minutes.\n * @param {number} [options.refreshAge=maxage] The maximum age of data\n * (in milliseconds), after which their refreshment will be triggered when\n * any component referencing them via `useAsyncData()` hook (re-)renders.\n * Defaults to `maxage` value.\n * @return {{\n * data: any,\n * loading: boolean,\n * timestamp: number\n * }} Returns an object with three fields: `data` holds the actual result of\n * last `loader` invokation, if any, and if satisfies `maxage` limit; `loading`\n * is a boolean flag, which is `true` if data are being loaded (the hook is\n * waiting for `loader` function resolution); `timestamp` (in milliseconds)\n * is Unix timestamp of related data currently loaded into the global state.\n *\n * Note that loaded data, if any, are stored at the given `path` of global state\n * along with related meta-information, using slightly different state segment\n * structure (see {@link AsyncDataEnvelope}). That segment of the global state\n * can be accessed, and even modified using other hooks,\n * _e.g._ {@link useGlobalState}, but doing so you may interfere with related\n * `useAsyncData()` hooks logic.\n */\nexport default function useAsyncCollection(\n id,\n path,\n loader,\n options = {},\n) {\n const itemPath = path ? `${path}.${id}` : id;\n return useAsyncData(itemPath, (oldData) => loader(id, oldData), options);\n}\n"],"file":"useAsyncCollection.js"}
|
|
@@ -74,40 +74,7 @@ async function load(path, loader, globalState, oldData, opIdPrefix = 'C') {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
|
-
*
|
|
78
|
-
* global state segment, created by {@link useAsyncData} hook for storing of
|
|
79
|
-
* loaded async data, and associated metadata.
|
|
80
|
-
* @prop {any} data The actual loaded data.
|
|
81
|
-
* @prop {number} numRefs The count of currently mounted components referencing
|
|
82
|
-
* the data via `useAsyncData` hooks.
|
|
83
|
-
* @prop {string} operationId A unique ID of the current data loading
|
|
84
|
-
* operation, if one is in progress. Changing this ID before the operation
|
|
85
|
-
* completes effectively cancels the ongoing operation, and instructs related
|
|
86
|
-
* hook to ignore the operation result.
|
|
87
|
-
*
|
|
88
|
-
* NOTE: Server-side and client-side operation UIDs start with `S` or `C` letter
|
|
89
|
-
* respectively. At the client side, if an envelop stores `operationId` starting
|
|
90
|
-
* with `S` letter, it is understood as a non-terminated data loading operation
|
|
91
|
-
* during SSR, and it is restarted at the client-side in this case.
|
|
92
|
-
* @prop {number} timestamp Unix timestamp (in milliseconds) of the most
|
|
93
|
-
* recently loaded `data`.
|
|
94
|
-
*/
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* @typedef {function} AsyncDataLoader This type documents the signature of
|
|
98
|
-
* async data loader function, expected by {@link useAsyncData} hook.
|
|
99
|
-
* @param {any} oldData Previously loaded data (_i.e._ the data currently stored
|
|
100
|
-
* at the global state `path` managed by the corresponding `useAsyncData` hook,
|
|
101
|
-
* which are assumed to be resolved from a previous call to the loader).
|
|
102
|
-
* The loader does not have to use this argument, it is provided just for
|
|
103
|
-
* convenience.
|
|
104
|
-
* @return {Promise<any>} Async data to store to the state.
|
|
105
|
-
*/
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* @category Hooks
|
|
109
|
-
* @func useAsyncData
|
|
110
|
-
* @desc Resolves asynchronous data, and stores them at given `path` of global
|
|
77
|
+
* Resolves asynchronous data, and stores them at given `path` of global
|
|
111
78
|
* state. When multiple components rely on asynchronous data at the same `path`,
|
|
112
79
|
* the data are resolved once, and reused until their age is within specified
|
|
113
80
|
* bounds. Once the data are stale, the hook allows to refresh them. It also
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/useAsyncData.js"],"names":["DEFAULT_MAXAGE","load","path","loader","globalState","oldData","opIdPrefix","process","env","NODE_ENV","console","log","operationId","operationIdPath","set","data","get","state","groupCollapsed","timestamp","Date","now","groupEnd","useAsyncData","options","garbageCollectAge","maxage","refreshAge","undefined","localState","numRefs","ssrContext","noSSR","pending","push","numRefsPath","loadTriggered","charAt","deps","length","loading","Boolean"],"mappings":";;;;;;;;;AAIA;;AACA;;AACA;;AAEA;;AACA;;AACA;;AAVA;AACA;AACA;AAUA,MAAMA,cAAc,GAAG,IAAI,EAAJ,GAAS,IAAhC,C,CAAsC;;AAEtC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA,eAAeC,IAAf,CAAoBC,IAApB,EAA0BC,MAA1B,EAAkCC,WAAlC,EAA+CC,OAA/C,EAAwDC,UAAU,GAAG,GAArE,EAA0E;AACxE,MAAIC,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IAAyC,yBAA7C,EAA4D;AAC1D;AACAC,IAAAA,OAAO,CAACC,GAAR,CACG,4DAA2DT,IAAI,IAAI,EAAG,GADzE;AAGA;AACD;;AACD,QAAMU,WAAW,GAAGN,UAAU,GAAG,eAAjC;AACA,QAAMO,eAAe,GAAGX,IAAI,GAAI,GAAEA,IAAK,cAAX,GAA2B,aAAvD;AACAE,EAAAA,WAAW,CAACU,GAAZ,CAAgBD,eAAhB,EAAiCD,WAAjC;AACA,QAAMG,IAAI,GAAG,MAAMZ,MAAM,CAACE,OAAO,IAAID,WAAW,CAACY,GAAZ,CAAgBd,IAAhB,EAAsBa,IAAlC,CAAzB;AACA,QAAME,KAAK,GAAGb,WAAW,CAACY,GAAZ,CAAgBd,IAAhB,CAAd;;AACA,MAAIU,WAAW,KAAKK,KAAK,CAACL,WAA1B,EAAuC;AACrC,QAAIL,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IAAyC,yBAA7C,EAA4D;AAC1D;AACAC,MAAAA,OAAO,CAACQ,cAAR,CACG,2DACChB,IAAI,IAAI,EACT,GAHH;AAKAQ,MAAAA,OAAO,CAACC,GAAR,CAAY,OAAZ,EAAqB,uBAAUI,IAAV,CAArB;AACA;AACD;;AACDX,IAAAA,WAAW,CAACU,GAAZ,CAAgBZ,IAAhB,EAAsB,EACpB,GAAGe,KADiB;AAEpBF,MAAAA,IAFoB;AAGpBH,MAAAA,WAAW,EAAE,EAHO;AAIpBO,MAAAA,SAAS,EAAEC,IAAI,CAACC,GAAL;AAJS,KAAtB;;AAMA,QAAId,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IAAyC,yBAA7C,EAA4D;AAC1D;AACAC,MAAAA,OAAO,CAACY,QAAR;AACA;AACD;AACF;AACF;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACe,SAASC,YAAT,CACbrB,IADa,EAEbC,MAFa,EAGbqB,OAAO,GAAG,EAHG,EAIb;AACA,MAAI;AAAEC,IAAAA,iBAAF;AAAqBC,IAAAA,MAArB;AAA6BC,IAAAA;AAA7B,MAA4CH,OAAhD;AACA,MAAIE,MAAM,KAAKE,SAAf,EAA0BF,MAAM,GAAG1B,cAAT;AAC1B,MAAI2B,UAAU,KAAKC,SAAnB,EAA8BD,UAAU,GAAGD,MAAb;AAC9B,MAAID,iBAAiB,KAAKG,SAA1B,EAAqCH,iBAAiB,GAAGC,MAApB;AAErC,QAAMtB,WAAW,GAAG,0CAApB;AACA,QAAM,CAACyB,UAAD,IAAe,6BAAe3B,IAAf,EAAqB;AACxCa,IAAAA,IAAI,EAAE,IADkC;AAExCe,IAAAA,OAAO,EAAE,CAF+B;AAGxClB,IAAAA,WAAW,EAAE,EAH2B;AAIxCO,IAAAA,SAAS,EAAE;AAJ6B,GAArB,CAArB;;AAOA,MAAIf,WAAW,CAAC2B,UAAZ,IAA0B,CAACP,OAAO,CAACQ,KAAvC,EAA8C;AAC5C,UAAMf,KAAK,GAAGb,WAAW,CAACY,GAAZ,CAAgBd,IAAhB,CAAd;;AACA,QAAI,CAACe,KAAK,CAACE,SAAP,IAAoB,CAACF,KAAK,CAACL,WAA/B,EAA4C;AAC1CR,MAAAA,WAAW,CAAC2B,UAAZ,CAAuBE,OAAvB,CAA+BC,IAA/B,CACEjC,IAAI,CAACC,IAAD,EAAOC,MAAP,EAAeC,WAAf,EAA4Ba,KAAK,CAACF,IAAlC,EAAwC,GAAxC,CADN;AAGD;AACF,GAPD,MAOO;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAAU,MAAM;AAAE;AAChB,YAAMoB,WAAW,GAAGjC,IAAI,GAAI,GAAEA,IAAK,UAAX,GAAuB,SAA/C;AACA,YAAM4B,OAAO,GAAG1B,WAAW,CAACY,GAAZ,CAAgBmB,WAAhB,CAAhB;AACA/B,MAAAA,WAAW,CAACU,GAAZ,CAAgBqB,WAAhB,EAA6BL,OAAO,GAAG,CAAvC;AACA,aAAO,MAAM;AACX,cAAMb,KAAK,GAAGb,WAAW,CAACY,GAAZ,CAAgBd,IAAhB,CAAd;;AACA,YACEe,KAAK,CAACa,OAAN,KAAkB,CAAlB,IACGL,iBAAiB,GAAGL,IAAI,CAACC,GAAL,KAAaJ,KAAK,CAACE,SAF5C,EAGE;AACA,cAAIZ,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IAAyC,yBAA7C,EAA4D;AAC1D;AACAC,YAAAA,OAAO,CAACC,GAAR,CACG,6DACCT,IAAI,IAAI,EACT,EAHH;AAKA;AACD;;AACDE,UAAAA,WAAW,CAACU,GAAZ,CAAgBZ,IAAhB,EAAsB,EACpB,GAAGe,KADiB;AAEpBF,YAAAA,IAAI,EAAE,IAFc;AAGpBe,YAAAA,OAAO,EAAE,CAHW;AAIpBX,YAAAA,SAAS,EAAE;AAJS,WAAtB;AAMD,SAnBD,MAmBOf,WAAW,CAACU,GAAZ,CAAgBqB,WAAhB,EAA6BlB,KAAK,CAACa,OAAN,GAAgB,CAA7C;AACR,OAtBD;AAuBD,KA3BD,EA2BG,CAACL,iBAAD,EAAoBrB,WAApB,EAAiCF,IAAjC,CA3BH,EAVK,CAuCL;AACA;AAEA;;AACA,QAAIkC,aAAa,GAAG,KAApB;AACA,0BAAU,MAAM;AAAE;AAChB,YAAMnB,KAAK,GAAGb,WAAW,CAACY,GAAZ,CAAgBd,IAAhB,CAAd;;AACA,UAAIyB,UAAU,GAAGP,IAAI,CAACC,GAAL,KAAaJ,KAAK,CAACE,SAAhC,KACA,CAACF,KAAK,CAACL,WAAP,IAAsBK,KAAK,CAACL,WAAN,CAAkByB,MAAlB,OAA+B,GADrD,CAAJ,EAC+D;AAC7DpC,QAAAA,IAAI,CAACC,IAAD,EAAOC,MAAP,EAAeC,WAAf,EAA4Ba,KAAK,CAACF,IAAlC,CAAJ;AACAqB,QAAAA,aAAa,GAAG,IAAhB,CAF6D,CAEvC;AACvB;AACF,KAPD;AASA,UAAME,IAAI,GAAGd,OAAO,CAACc,IAAR,IAAgB,EAA7B;AACA,0BAAU,MAAM;AAAE;AAChB,UAAI,CAACF,aAAD,IAAkBE,IAAI,CAACC,MAA3B,EAAmCtC,IAAI,CAACC,IAAD,EAAOC,MAAP,EAAeC,WAAf,CAAJ;AACpC,KAFD,EAEGkC,IAFH,EAtDK,CAwDK;AACX;;AAED,SAAO;AACLvB,IAAAA,IAAI,EAAEW,MAAM,GAAGN,IAAI,CAACC,GAAL,KAAaQ,UAAU,CAACV,SAAjC,GAA6C,IAA7C,GAAoDU,UAAU,CAACd,IADhE;AAELyB,IAAAA,OAAO,EAAEC,OAAO,CAACZ,UAAU,CAACjB,WAAZ,CAFX;AAGLO,IAAAA,SAAS,EAAEU,UAAU,CAACV;AAHjB,GAAP;AAKD","sourcesContent":["/**\n * Loads and uses async data into the GlobalState path.\n */\n\nimport { cloneDeep } from 'lodash';\nimport { useEffect } from 'react';\nimport { v4 as uuid } from 'uuid';\n\nimport { getGlobalState } from './GlobalStateProvider';\nimport useGlobalState from './useGlobalState';\nimport { isDebugMode } from './utils';\n\nconst DEFAULT_MAXAGE = 5 * 60 * 1000; // 5 minutes.\n\n/**\n * Executes the data loading operation.\n * @param {string} path Data segment path inside the global state.\n * @param {function} loader Data loader.\n * @param {GlobalState} globalState The global state instance.\n * @param {any} [oldData] Optional. Previously fetched data, currently stored in\n * the state, if already fetched by the caller; otherwise, they will be fetched\n * by the load() function itself.\n * @param {string} [opIdPrefix='C'] operationId prefix to use, which should be\n * 'C' at the client-side (default), or 'S' at the server-side (within SSR\n * context).\n * @return {Promise} Resolves once the operation is done.\n * @ignore\n */\nasync function load(path, loader, globalState, oldData, opIdPrefix = 'C') {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.log(\n `ReactGlobalState: useAsyncData data (re-)loading. Path: \"${path || ''}\"`,\n );\n /* eslint-enable no-console */\n }\n const operationId = opIdPrefix + uuid();\n const operationIdPath = path ? `${path}.operationId` : 'operationId';\n globalState.set(operationIdPath, operationId);\n const data = await loader(oldData || globalState.get(path).data);\n const state = globalState.get(path);\n if (operationId === state.operationId) {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupCollapsed(\n `ReactGlobalState: useAsyncData data (re-)loaded. Path: \"${\n path || ''\n }\"`,\n );\n console.log('Data:', cloneDeep(data));\n /* eslint-enable no-console */\n }\n globalState.set(path, {\n ...state,\n data,\n operationId: '',\n timestamp: Date.now(),\n });\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupEnd();\n /* eslint-enable no-console */\n }\n }\n}\n\n/**\n * @typedef {object} AsyncDataEnvelope This type documents the structure of\n * global state segment, created by {@link useAsyncData} hook for storing of\n * loaded async data, and associated metadata.\n * @prop {any} data The actual loaded data.\n * @prop {number} numRefs The count of currently mounted components referencing\n * the data via `useAsyncData` hooks.\n * @prop {string} operationId A unique ID of the current data loading\n * operation, if one is in progress. Changing this ID before the operation\n * completes effectively cancels the ongoing operation, and instructs related\n * hook to ignore the operation result.\n *\n * NOTE: Server-side and client-side operation UIDs start with `S` or `C` letter\n * respectively. At the client side, if an envelop stores `operationId` starting\n * with `S` letter, it is understood as a non-terminated data loading operation\n * during SSR, and it is restarted at the client-side in this case.\n * @prop {number} timestamp Unix timestamp (in milliseconds) of the most\n * recently loaded `data`.\n */\n\n/**\n * @typedef {function} AsyncDataLoader This type documents the signature of\n * async data loader function, expected by {@link useAsyncData} hook.\n * @param {any} oldData Previously loaded data (_i.e._ the data currently stored\n * at the global state `path` managed by the corresponding `useAsyncData` hook,\n * which are assumed to be resolved from a previous call to the loader).\n * The loader does not have to use this argument, it is provided just for\n * convenience.\n * @return {Promise<any>} Async data to store to the state.\n */\n\n/**\n * @category Hooks\n * @func useAsyncData\n * @desc Resolves asynchronous data, and stores them at given `path` of global\n * state. When multiple components rely on asynchronous data at the same `path`,\n * the data are resolved once, and reused until their age is within specified\n * bounds. Once the data are stale, the hook allows to refresh them. It also\n * garbage-collects stale data from the global state when the last component\n * relying on them is unmounted.\n * @param {string} path Dot-delimitered state path, where data envelop is\n * stored.\n * @param {AsyncDataLoader} loader Asynchronous function which resolves (loads)\n * data, which should be stored at the global state `path`. When multiple\n * components\n * use `useAsyncData()` hook for the same `path`, the library assumes that all\n * hook instances are called with the same `loader` (_i.e._ whichever of these\n * loaders is used to resolve async data, the result is acceptable to be reused\n * in all related components).\n * @param {object} [options] Additional options.\n * @param {any[]} [options.deps=[]] An array of dependencies, which trigger\n * data reload when changed. Given dependency changes are watched shallowly\n * (similarly to the standard React's\n * [useEffect()](https://reactjs.org/docs/hooks-reference.html#useeffect)).\n * @param {boolean} [options.noSSR] If `true`, this hook won't load data during\n * server-side rendering.\n * @param {number} [options.garbageCollectAge=maxage] The maximum age of data\n * (in milliseconds), after which they are dropped from the state when the last\n * component referencing them via `useAsyncData()` hook unmounts. Defaults to\n * `maxage` option value.\n * @param {number} [options.maxage=5 x 60 x 1000] The maximum age of\n * data (in milliseconds) acceptable to the hook's caller. If loaded data are\n * older than this value, `null` is returned instead. Defaults to 5 minutes.\n * @param {number} [options.refreshAge=maxage] The maximum age of data\n * (in milliseconds), after which their refreshment will be triggered when\n * any component referencing them via `useAsyncData()` hook (re-)renders.\n * Defaults to `maxage` value.\n * @return {{\n * data: any,\n * loading: boolean,\n * timestamp: number\n * }} Returns an object with three fields: `data` holds the actual result of\n * last `loader` invokation, if any, and if satisfies `maxage` limit; `loading`\n * is a boolean flag, which is `true` if data are being loaded (the hook is\n * waiting for `loader` function resolution); `timestamp` (in milliseconds)\n * is Unix timestamp of related data currently loaded into the global state.\n *\n * Note that loaded data, if any, are stored at the given `path` of global state\n * along with related meta-information, using slightly different state segment\n * structure (see {@link AsyncDataEnvelope}). That segment of the global state\n * can be accessed, and even modified using other hooks,\n * _e.g._ {@link useGlobalState}, but doing so you may interfere with related\n * `useAsyncData()` hooks logic.\n */\nexport default function useAsyncData(\n path,\n loader,\n options = {},\n) {\n let { garbageCollectAge, maxage, refreshAge } = options;\n if (maxage === undefined) maxage = DEFAULT_MAXAGE;\n if (refreshAge === undefined) refreshAge = maxage;\n if (garbageCollectAge === undefined) garbageCollectAge = maxage;\n\n const globalState = getGlobalState();\n const [localState] = useGlobalState(path, {\n data: null,\n numRefs: 0,\n operationId: '',\n timestamp: 0,\n });\n\n if (globalState.ssrContext && !options.noSSR) {\n const state = globalState.get(path);\n if (!state.timestamp && !state.operationId) {\n globalState.ssrContext.pending.push(\n load(path, loader, globalState, state.data, 'S'),\n );\n }\n } else {\n // This takes care about the client-side reference counting, and garbage\n // collection.\n //\n // Note: the Rules of Hook below are violated by conditional call to a hook,\n // but as the condition is actually server-side or client-side environment,\n // it is effectively non-conditional at the runtime.\n //\n // TODO: Though, maybe there is a way to refactor it into a cleaner code.\n // The same applies to other useEffect() hooks below.\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n const numRefsPath = path ? `${path}.numRefs` : 'numRefs';\n const numRefs = globalState.get(numRefsPath);\n globalState.set(numRefsPath, numRefs + 1);\n return () => {\n const state = globalState.get(path);\n if (\n state.numRefs === 1\n && garbageCollectAge < Date.now() - state.timestamp\n ) {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.log(\n `ReactGlobalState - useAsyncData garbage collected at path ${\n path || ''\n }`,\n );\n /* eslint-enable no-console */\n }\n globalState.set(path, {\n ...state,\n data: null,\n numRefs: 0,\n timestamp: 0,\n });\n } else globalState.set(numRefsPath, state.numRefs - 1);\n };\n }, [garbageCollectAge, globalState, path]);\n\n // Note: a bunch of Rules of Hooks ignored belows because in our very\n // special case the otherwise wrong behavior is actually what we need.\n\n // Data loading and refreshing.\n let loadTriggered = false;\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n const state = globalState.get(path);\n if (refreshAge < Date.now() - state.timestamp\n && (!state.operationId || state.operationId.charAt() === 'S')) {\n load(path, loader, globalState, state.data);\n loadTriggered = true; // eslint-disable-line react-hooks/exhaustive-deps\n }\n });\n\n const deps = options.deps || [];\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n if (!loadTriggered && deps.length) load(path, loader, globalState);\n }, deps); // eslint-disable-line react-hooks/exhaustive-deps\n }\n\n return {\n data: maxage < Date.now() - localState.timestamp ? null : localState.data,\n loading: Boolean(localState.operationId),\n timestamp: localState.timestamp,\n };\n}\n"],"file":"useAsyncData.js"}
|
|
1
|
+
{"version":3,"sources":["../../src/useAsyncData.js"],"names":["DEFAULT_MAXAGE","load","path","loader","globalState","oldData","opIdPrefix","process","env","NODE_ENV","console","log","operationId","operationIdPath","set","data","get","state","groupCollapsed","timestamp","Date","now","groupEnd","useAsyncData","options","garbageCollectAge","maxage","refreshAge","undefined","localState","numRefs","ssrContext","noSSR","pending","push","numRefsPath","loadTriggered","charAt","deps","length","loading","Boolean"],"mappings":";;;;;;;;;AAIA;;AACA;;AACA;;AAEA;;AACA;;AACA;;AAVA;AACA;AACA;AAUA,MAAMA,cAAc,GAAG,IAAI,EAAJ,GAAS,IAAhC,C,CAAsC;;AAEtC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA,eAAeC,IAAf,CAAoBC,IAApB,EAA0BC,MAA1B,EAAkCC,WAAlC,EAA+CC,OAA/C,EAAwDC,UAAU,GAAG,GAArE,EAA0E;AACxE,MAAIC,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IAAyC,yBAA7C,EAA4D;AAC1D;AACAC,IAAAA,OAAO,CAACC,GAAR,CACG,4DAA2DT,IAAI,IAAI,EAAG,GADzE;AAGA;AACD;;AACD,QAAMU,WAAW,GAAGN,UAAU,GAAG,eAAjC;AACA,QAAMO,eAAe,GAAGX,IAAI,GAAI,GAAEA,IAAK,cAAX,GAA2B,aAAvD;AACAE,EAAAA,WAAW,CAACU,GAAZ,CAAgBD,eAAhB,EAAiCD,WAAjC;AACA,QAAMG,IAAI,GAAG,MAAMZ,MAAM,CAACE,OAAO,IAAID,WAAW,CAACY,GAAZ,CAAgBd,IAAhB,EAAsBa,IAAlC,CAAzB;AACA,QAAME,KAAK,GAAGb,WAAW,CAACY,GAAZ,CAAgBd,IAAhB,CAAd;;AACA,MAAIU,WAAW,KAAKK,KAAK,CAACL,WAA1B,EAAuC;AACrC,QAAIL,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IAAyC,yBAA7C,EAA4D;AAC1D;AACAC,MAAAA,OAAO,CAACQ,cAAR,CACG,2DACChB,IAAI,IAAI,EACT,GAHH;AAKAQ,MAAAA,OAAO,CAACC,GAAR,CAAY,OAAZ,EAAqB,uBAAUI,IAAV,CAArB;AACA;AACD;;AACDX,IAAAA,WAAW,CAACU,GAAZ,CAAgBZ,IAAhB,EAAsB,EACpB,GAAGe,KADiB;AAEpBF,MAAAA,IAFoB;AAGpBH,MAAAA,WAAW,EAAE,EAHO;AAIpBO,MAAAA,SAAS,EAAEC,IAAI,CAACC,GAAL;AAJS,KAAtB;;AAMA,QAAId,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IAAyC,yBAA7C,EAA4D;AAC1D;AACAC,MAAAA,OAAO,CAACY,QAAR;AACA;AACD;AACF;AACF;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACe,SAASC,YAAT,CACbrB,IADa,EAEbC,MAFa,EAGbqB,OAAO,GAAG,EAHG,EAIb;AACA,MAAI;AAAEC,IAAAA,iBAAF;AAAqBC,IAAAA,MAArB;AAA6BC,IAAAA;AAA7B,MAA4CH,OAAhD;AACA,MAAIE,MAAM,KAAKE,SAAf,EAA0BF,MAAM,GAAG1B,cAAT;AAC1B,MAAI2B,UAAU,KAAKC,SAAnB,EAA8BD,UAAU,GAAGD,MAAb;AAC9B,MAAID,iBAAiB,KAAKG,SAA1B,EAAqCH,iBAAiB,GAAGC,MAApB;AAErC,QAAMtB,WAAW,GAAG,0CAApB;AACA,QAAM,CAACyB,UAAD,IAAe,6BAAe3B,IAAf,EAAqB;AACxCa,IAAAA,IAAI,EAAE,IADkC;AAExCe,IAAAA,OAAO,EAAE,CAF+B;AAGxClB,IAAAA,WAAW,EAAE,EAH2B;AAIxCO,IAAAA,SAAS,EAAE;AAJ6B,GAArB,CAArB;;AAOA,MAAIf,WAAW,CAAC2B,UAAZ,IAA0B,CAACP,OAAO,CAACQ,KAAvC,EAA8C;AAC5C,UAAMf,KAAK,GAAGb,WAAW,CAACY,GAAZ,CAAgBd,IAAhB,CAAd;;AACA,QAAI,CAACe,KAAK,CAACE,SAAP,IAAoB,CAACF,KAAK,CAACL,WAA/B,EAA4C;AAC1CR,MAAAA,WAAW,CAAC2B,UAAZ,CAAuBE,OAAvB,CAA+BC,IAA/B,CACEjC,IAAI,CAACC,IAAD,EAAOC,MAAP,EAAeC,WAAf,EAA4Ba,KAAK,CAACF,IAAlC,EAAwC,GAAxC,CADN;AAGD;AACF,GAPD,MAOO;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAAU,MAAM;AAAE;AAChB,YAAMoB,WAAW,GAAGjC,IAAI,GAAI,GAAEA,IAAK,UAAX,GAAuB,SAA/C;AACA,YAAM4B,OAAO,GAAG1B,WAAW,CAACY,GAAZ,CAAgBmB,WAAhB,CAAhB;AACA/B,MAAAA,WAAW,CAACU,GAAZ,CAAgBqB,WAAhB,EAA6BL,OAAO,GAAG,CAAvC;AACA,aAAO,MAAM;AACX,cAAMb,KAAK,GAAGb,WAAW,CAACY,GAAZ,CAAgBd,IAAhB,CAAd;;AACA,YACEe,KAAK,CAACa,OAAN,KAAkB,CAAlB,IACGL,iBAAiB,GAAGL,IAAI,CAACC,GAAL,KAAaJ,KAAK,CAACE,SAF5C,EAGE;AACA,cAAIZ,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IAAyC,yBAA7C,EAA4D;AAC1D;AACAC,YAAAA,OAAO,CAACC,GAAR,CACG,6DACCT,IAAI,IAAI,EACT,EAHH;AAKA;AACD;;AACDE,UAAAA,WAAW,CAACU,GAAZ,CAAgBZ,IAAhB,EAAsB,EACpB,GAAGe,KADiB;AAEpBF,YAAAA,IAAI,EAAE,IAFc;AAGpBe,YAAAA,OAAO,EAAE,CAHW;AAIpBX,YAAAA,SAAS,EAAE;AAJS,WAAtB;AAMD,SAnBD,MAmBOf,WAAW,CAACU,GAAZ,CAAgBqB,WAAhB,EAA6BlB,KAAK,CAACa,OAAN,GAAgB,CAA7C;AACR,OAtBD;AAuBD,KA3BD,EA2BG,CAACL,iBAAD,EAAoBrB,WAApB,EAAiCF,IAAjC,CA3BH,EAVK,CAuCL;AACA;AAEA;;AACA,QAAIkC,aAAa,GAAG,KAApB;AACA,0BAAU,MAAM;AAAE;AAChB,YAAMnB,KAAK,GAAGb,WAAW,CAACY,GAAZ,CAAgBd,IAAhB,CAAd;;AACA,UAAIyB,UAAU,GAAGP,IAAI,CAACC,GAAL,KAAaJ,KAAK,CAACE,SAAhC,KACA,CAACF,KAAK,CAACL,WAAP,IAAsBK,KAAK,CAACL,WAAN,CAAkByB,MAAlB,OAA+B,GADrD,CAAJ,EAC+D;AAC7DpC,QAAAA,IAAI,CAACC,IAAD,EAAOC,MAAP,EAAeC,WAAf,EAA4Ba,KAAK,CAACF,IAAlC,CAAJ;AACAqB,QAAAA,aAAa,GAAG,IAAhB,CAF6D,CAEvC;AACvB;AACF,KAPD;AASA,UAAME,IAAI,GAAGd,OAAO,CAACc,IAAR,IAAgB,EAA7B;AACA,0BAAU,MAAM;AAAE;AAChB,UAAI,CAACF,aAAD,IAAkBE,IAAI,CAACC,MAA3B,EAAmCtC,IAAI,CAACC,IAAD,EAAOC,MAAP,EAAeC,WAAf,CAAJ;AACpC,KAFD,EAEGkC,IAFH,EAtDK,CAwDK;AACX;;AAED,SAAO;AACLvB,IAAAA,IAAI,EAAEW,MAAM,GAAGN,IAAI,CAACC,GAAL,KAAaQ,UAAU,CAACV,SAAjC,GAA6C,IAA7C,GAAoDU,UAAU,CAACd,IADhE;AAELyB,IAAAA,OAAO,EAAEC,OAAO,CAACZ,UAAU,CAACjB,WAAZ,CAFX;AAGLO,IAAAA,SAAS,EAAEU,UAAU,CAACV;AAHjB,GAAP;AAKD","sourcesContent":["/**\n * Loads and uses async data into the GlobalState path.\n */\n\nimport { cloneDeep } from 'lodash';\nimport { useEffect } from 'react';\nimport { v4 as uuid } from 'uuid';\n\nimport { getGlobalState } from './GlobalStateProvider';\nimport useGlobalState from './useGlobalState';\nimport { isDebugMode } from './utils';\n\nconst DEFAULT_MAXAGE = 5 * 60 * 1000; // 5 minutes.\n\n/**\n * Executes the data loading operation.\n * @param {string} path Data segment path inside the global state.\n * @param {function} loader Data loader.\n * @param {GlobalState} globalState The global state instance.\n * @param {any} [oldData] Optional. Previously fetched data, currently stored in\n * the state, if already fetched by the caller; otherwise, they will be fetched\n * by the load() function itself.\n * @param {string} [opIdPrefix='C'] operationId prefix to use, which should be\n * 'C' at the client-side (default), or 'S' at the server-side (within SSR\n * context).\n * @return {Promise} Resolves once the operation is done.\n * @ignore\n */\nasync function load(path, loader, globalState, oldData, opIdPrefix = 'C') {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.log(\n `ReactGlobalState: useAsyncData data (re-)loading. Path: \"${path || ''}\"`,\n );\n /* eslint-enable no-console */\n }\n const operationId = opIdPrefix + uuid();\n const operationIdPath = path ? `${path}.operationId` : 'operationId';\n globalState.set(operationIdPath, operationId);\n const data = await loader(oldData || globalState.get(path).data);\n const state = globalState.get(path);\n if (operationId === state.operationId) {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupCollapsed(\n `ReactGlobalState: useAsyncData data (re-)loaded. Path: \"${\n path || ''\n }\"`,\n );\n console.log('Data:', cloneDeep(data));\n /* eslint-enable no-console */\n }\n globalState.set(path, {\n ...state,\n data,\n operationId: '',\n timestamp: Date.now(),\n });\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupEnd();\n /* eslint-enable no-console */\n }\n }\n}\n\n/**\n * Resolves asynchronous data, and stores them at given `path` of global\n * state. When multiple components rely on asynchronous data at the same `path`,\n * the data are resolved once, and reused until their age is within specified\n * bounds. Once the data are stale, the hook allows to refresh them. It also\n * garbage-collects stale data from the global state when the last component\n * relying on them is unmounted.\n * @param {string} path Dot-delimitered state path, where data envelop is\n * stored.\n * @param {AsyncDataLoader} loader Asynchronous function which resolves (loads)\n * data, which should be stored at the global state `path`. When multiple\n * components\n * use `useAsyncData()` hook for the same `path`, the library assumes that all\n * hook instances are called with the same `loader` (_i.e._ whichever of these\n * loaders is used to resolve async data, the result is acceptable to be reused\n * in all related components).\n * @param {object} [options] Additional options.\n * @param {any[]} [options.deps=[]] An array of dependencies, which trigger\n * data reload when changed. Given dependency changes are watched shallowly\n * (similarly to the standard React's\n * [useEffect()](https://reactjs.org/docs/hooks-reference.html#useeffect)).\n * @param {boolean} [options.noSSR] If `true`, this hook won't load data during\n * server-side rendering.\n * @param {number} [options.garbageCollectAge=maxage] The maximum age of data\n * (in milliseconds), after which they are dropped from the state when the last\n * component referencing them via `useAsyncData()` hook unmounts. Defaults to\n * `maxage` option value.\n * @param {number} [options.maxage=5 x 60 x 1000] The maximum age of\n * data (in milliseconds) acceptable to the hook's caller. If loaded data are\n * older than this value, `null` is returned instead. Defaults to 5 minutes.\n * @param {number} [options.refreshAge=maxage] The maximum age of data\n * (in milliseconds), after which their refreshment will be triggered when\n * any component referencing them via `useAsyncData()` hook (re-)renders.\n * Defaults to `maxage` value.\n * @return {{\n * data: any,\n * loading: boolean,\n * timestamp: number\n * }} Returns an object with three fields: `data` holds the actual result of\n * last `loader` invokation, if any, and if satisfies `maxage` limit; `loading`\n * is a boolean flag, which is `true` if data are being loaded (the hook is\n * waiting for `loader` function resolution); `timestamp` (in milliseconds)\n * is Unix timestamp of related data currently loaded into the global state.\n *\n * Note that loaded data, if any, are stored at the given `path` of global state\n * along with related meta-information, using slightly different state segment\n * structure (see {@link AsyncDataEnvelope}). That segment of the global state\n * can be accessed, and even modified using other hooks,\n * _e.g._ {@link useGlobalState}, but doing so you may interfere with related\n * `useAsyncData()` hooks logic.\n */\nexport default function useAsyncData(\n path,\n loader,\n options = {},\n) {\n let { garbageCollectAge, maxage, refreshAge } = options;\n if (maxage === undefined) maxage = DEFAULT_MAXAGE;\n if (refreshAge === undefined) refreshAge = maxage;\n if (garbageCollectAge === undefined) garbageCollectAge = maxage;\n\n const globalState = getGlobalState();\n const [localState] = useGlobalState(path, {\n data: null,\n numRefs: 0,\n operationId: '',\n timestamp: 0,\n });\n\n if (globalState.ssrContext && !options.noSSR) {\n const state = globalState.get(path);\n if (!state.timestamp && !state.operationId) {\n globalState.ssrContext.pending.push(\n load(path, loader, globalState, state.data, 'S'),\n );\n }\n } else {\n // This takes care about the client-side reference counting, and garbage\n // collection.\n //\n // Note: the Rules of Hook below are violated by conditional call to a hook,\n // but as the condition is actually server-side or client-side environment,\n // it is effectively non-conditional at the runtime.\n //\n // TODO: Though, maybe there is a way to refactor it into a cleaner code.\n // The same applies to other useEffect() hooks below.\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n const numRefsPath = path ? `${path}.numRefs` : 'numRefs';\n const numRefs = globalState.get(numRefsPath);\n globalState.set(numRefsPath, numRefs + 1);\n return () => {\n const state = globalState.get(path);\n if (\n state.numRefs === 1\n && garbageCollectAge < Date.now() - state.timestamp\n ) {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.log(\n `ReactGlobalState - useAsyncData garbage collected at path ${\n path || ''\n }`,\n );\n /* eslint-enable no-console */\n }\n globalState.set(path, {\n ...state,\n data: null,\n numRefs: 0,\n timestamp: 0,\n });\n } else globalState.set(numRefsPath, state.numRefs - 1);\n };\n }, [garbageCollectAge, globalState, path]);\n\n // Note: a bunch of Rules of Hooks ignored belows because in our very\n // special case the otherwise wrong behavior is actually what we need.\n\n // Data loading and refreshing.\n let loadTriggered = false;\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n const state = globalState.get(path);\n if (refreshAge < Date.now() - state.timestamp\n && (!state.operationId || state.operationId.charAt() === 'S')) {\n load(path, loader, globalState, state.data);\n loadTriggered = true; // eslint-disable-line react-hooks/exhaustive-deps\n }\n });\n\n const deps = options.deps || [];\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n if (!loadTriggered && deps.length) load(path, loader, globalState);\n }, deps); // eslint-disable-line react-hooks/exhaustive-deps\n }\n\n return {\n data: maxage < Date.now() - localState.timestamp ? null : localState.data,\n loading: Boolean(localState.operationId),\n timestamp: localState.timestamp,\n };\n}\n"],"file":"useAsyncData.js"}
|
|
@@ -16,9 +16,7 @@ var _utils = require("./utils");
|
|
|
16
16
|
// Hook for updates of global state.
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
* @func useGlobalState
|
|
21
|
-
* @desc The primary hook for interacting with the global state, modeled after
|
|
19
|
+
* The primary hook for interacting with the global state, modeled after
|
|
22
20
|
* the standard React's
|
|
23
21
|
* [useState](https://reactjs.org/docs/hooks-reference.html#usestate).
|
|
24
22
|
* It subscribes a component to a given `path` of global state, and provides
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/useGlobalState.js"],"names":["useGlobalState","path","initialValue","globalState","state","get","value","set","localState","setLocalState","callback","active","newState","watch","unWatch","ref","current","setter","newValue","process","env","NODE_ENV","console","groupCollapsed","log","groupEnd"],"mappings":";;;;;;;AAEA;;AACA;;AAEA;;AACA;;AANA;;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;
|
|
1
|
+
{"version":3,"sources":["../../src/useGlobalState.js"],"names":["useGlobalState","path","initialValue","globalState","state","get","value","set","localState","setLocalState","callback","active","newState","watch","unWatch","ref","current","setter","newValue","process","env","NODE_ENV","console","groupCollapsed","log","groupEnd"],"mappings":"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","sourcesContent":["// Hook for updates of global state.\n\nimport { cloneDeep, isFunction, isUndefined } from 'lodash';\nimport { useEffect, useRef, useState } from 'react';\n\nimport { getGlobalState } from './GlobalStateProvider';\nimport { isDebugMode } from './utils';\n\n/**\n * The primary hook for interacting with the global state, modeled after\n * the standard React's\n * [useState](https://reactjs.org/docs/hooks-reference.html#usestate).\n * It subscribes a component to a given `path` of global state, and provides\n * a function to update it. Each time the value at `path` changes, the hook\n * triggers re-render of its host component.\n *\n * **Note:**\n * - For performance, the library does not copy objects written to / read from\n * global state paths. You MUST NOT manually mutate returned state values,\n * or change objects already written into the global state, without explicitly\n * clonning them first yourself.\n * - State update notifications are asynchronous. When your code does multiple\n * global state updates in the same React rendering cycle, all state update\n * notifications are queued and dispatched together, after the current\n * rendering cycle. In other words, in any given rendering cycle the global\n * state values are \"fixed\", and all changes becomes visible at once in the\n * next triggered rendering pass.\n *\n * @param {string} [path] Dot-delimitered state path. It can be undefined to\n * subscribe for entire state.\n *\n * Under-the-hood state values are read and written using `lodash`\n * [_.get()](https://lodash.com/docs/4.17.15#get) and\n * [_.set()](https://lodash.com/docs/4.17.15#set) methods, thus it is safe\n * to access state paths which have not been created before.\n * @param {any} [initialValue] Initial value to set at the `path`, or its\n * factory:\n * - If a function is given, it will act similar to\n * [the lazy initial state of the standard React's useState()](https://reactjs.org/docs/hooks-reference.html#lazy-initial-state):\n * only if the value at `path` is `undefined`, the function will be executed,\n * and the value it returns will be written to the `path`.\n * - Otherwise, the given value itself will be written to the `path`,\n * if the current value at `path` is `undefined`.\n * @return {Array} It returs an array with two elements: `[value, setValue]`:\n *\n * - The `value` is the current value at given `path`.\n *\n * - The `setValue()` is setter function to write a new value to the `path`.\n *\n * Similar to the standard React's `useState()`, it supports\n * [functional value updates](https://reactjs.org/docs/hooks-reference.html#functional-updates):\n * if `setValue()` is called with a function as argument, that function will\n * be called and its return value will be written to `path`. Otherwise,\n * the argument of `setValue()` itself is written to `path`.\n *\n * Also, similar to the standard React's state setters, `setValue()` is\n * stable function: it does not change between component re-renders.\n */\nexport default function useGlobalState(path, initialValue) {\n const globalState = getGlobalState();\n let state = globalState.get(path);\n if (isUndefined(state) && !isUndefined(initialValue)) {\n const value = isFunction(initialValue) ? initialValue() : initialValue;\n state = globalState.set(path, value);\n }\n const [\n localState,\n setLocalState,\n ] = useState(() => state);\n\n useEffect(() => {\n // Note: the \"callback.active\" flag below is needed to workaround the issue\n // https://github.com/birdofpreyru/react-global-state/issues/33,\n // which, unfortunately, I am not able to reproduce in test environment,\n // but I definitely seen it in the wild.\n const callback = () => {\n if (callback.active) {\n const newState = globalState.get(path);\n if (newState !== localState) setLocalState(() => newState);\n }\n };\n callback.active = true;\n globalState.watch(callback);\n callback();\n return () => {\n delete callback.active;\n globalState.unWatch(callback);\n };\n }, [globalState, localState, path]);\n\n const ref = useRef();\n if (!ref.current) {\n ref.current = {\n localState,\n path,\n setter: (value) => {\n const newValue = isFunction(value)\n ? value(ref.current.localState) : value;\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupCollapsed(\n `ReactGlobalState - useGlobalState setter triggered for path ${\n ref.current.path || ''\n }`,\n );\n console.log('New value:', cloneDeep(newValue));\n console.groupEnd();\n /* eslint-enable no-console */\n }\n\n globalState.set(ref.current.path, newValue);\n\n // The update of local state here is important for managed inputs:\n // if we wait until the global state change notification is delivered\n // (which happens after the next React render), React won't conserve\n // the text cursor inside the currently focused input field (the cursor\n // will jump to the field end, like if the value was changed not by\n // keyboard input).\n setLocalState(() => newValue);\n },\n };\n } else {\n ref.current.localState = localState;\n ref.current.path = path;\n }\n\n return [\n localState,\n ref.current.setter,\n ];\n}\n"],"file":"useGlobalState.js"}
|
package/build/node/utils.js
CHANGED
|
@@ -8,18 +8,6 @@ exports.isDebugMode = isDebugMode;
|
|
|
8
8
|
|
|
9
9
|
// Auxiliary stuff.
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* @global
|
|
13
|
-
* @const REACT_GLOBAL_STATE_DEBUG
|
|
14
|
-
* @desc `REACT_GLOBAL_STATE_DEBUG` is an environment variable you can set
|
|
15
|
-
* to any truthy value for Node (or, in case of client-side bundle, inject
|
|
16
|
-
* using Webpack's
|
|
17
|
-
* [EnvironmentPlugin](https://webpack.js.org/plugins/environment-plugin/))
|
|
18
|
-
* to enable debug logging by `react-global-state` library. In either case,
|
|
19
|
-
* the logging is enabled in non-production code only, which is tested by
|
|
20
|
-
* `process.env.NODE_ENV` value being distinct from `production`.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
11
|
/**
|
|
24
12
|
* Returns 'true' if debug logging should be performed; 'false' otherwise.
|
|
25
13
|
*
|
package/build/node/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils.js"],"names":["isDebugMode","process","env","NODE_ENV","REACT_GLOBAL_STATE_DEBUG","error"],"mappings":";;;;;;;;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA
|
|
1
|
+
{"version":3,"sources":["../../src/utils.js"],"names":["isDebugMode","process","env","NODE_ENV","REACT_GLOBAL_STATE_DEBUG","error"],"mappings":";;;;;;;;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASA,WAAT,GAAuB;AAC5B,MAAI;AACF,WAAOC,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IACF,CAAC,CAACF,OAAO,CAACC,GAAR,CAAYE,wBADnB;AAED,GAHD,CAGE,OAAOC,KAAP,EAAc;AACd,WAAO,KAAP;AACD;AACF;;eAEc,I","sourcesContent":["// Auxiliary stuff.\n\n/**\n * Returns 'true' if debug logging should be performed; 'false' otherwise.\n *\n * BEWARE: The actual safeguards for the debug logging still should read\n * if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n * // Some debug logging\n * }\n * to ensure that debug code is stripped out by Webpack in production mode.\n *\n * @returns {boolean}\n * @ignore\n */\nexport function isDebugMode() {\n try {\n return process.env.NODE_ENV !== 'production'\n && !!process.env.REACT_GLOBAL_STATE_DEBUG;\n } catch (error) {\n return false;\n }\n}\n\nexport default null;\n"],"file":"utils.js"}
|
package/build/web/GlobalState.js
CHANGED
|
@@ -19,9 +19,9 @@ var _lodash = require("lodash");
|
|
|
19
19
|
|
|
20
20
|
var _utils = require("./utils");
|
|
21
21
|
|
|
22
|
-
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object);
|
|
22
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
23
23
|
|
|
24
|
-
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]
|
|
24
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
25
25
|
|
|
26
26
|
var ERR_NO_SSR_WATCH = 'GlobalState must not be watched at server side';
|
|
27
27
|
/**
|
|
@@ -37,9 +37,7 @@ function fullPath(statePath) {
|
|
|
37
37
|
|
|
38
38
|
var GlobalState = /*#__PURE__*/function () {
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
41
|
-
* @classdesc Represents global state objects.
|
|
42
|
-
* @desc Creates a new global state object.
|
|
40
|
+
* Creates a new global state object.
|
|
43
41
|
* @param {any} [initialState] Intial global state content.
|
|
44
42
|
* @param {SsrContext} [ssrContext] Server-side rendering context.
|
|
45
43
|
*/
|
|
@@ -71,9 +69,7 @@ var GlobalState = /*#__PURE__*/function () {
|
|
|
71
69
|
|
|
72
70
|
}
|
|
73
71
|
/**
|
|
74
|
-
*
|
|
75
|
-
* @memberof GlobalState
|
|
76
|
-
* @desc Gets the value at given `path` of global state. If `path` is null or
|
|
72
|
+
* Gets the value at given `path` of global state. If `path` is null or
|
|
77
73
|
* undefined, the entire state object is returned.
|
|
78
74
|
* @param {string} [path] Dot-delimitered state path. If not given, entire
|
|
79
75
|
* global state content is returned.
|
|
@@ -87,9 +83,7 @@ var GlobalState = /*#__PURE__*/function () {
|
|
|
87
83
|
return (0, _lodash.get)(this, fullPath(path));
|
|
88
84
|
}
|
|
89
85
|
/**
|
|
90
|
-
*
|
|
91
|
-
* @memberof GlobalState
|
|
92
|
-
* @desc Writes the `value` to given global state `path`.
|
|
86
|
+
* Writes the `value` to given global state `path`.
|
|
93
87
|
* @param {string} [path] Dot-delimitered state path. If not given, entire
|
|
94
88
|
* global state content is replaced by the `value`.
|
|
95
89
|
* @param {any} value The value.
|
|
@@ -151,9 +145,7 @@ var GlobalState = /*#__PURE__*/function () {
|
|
|
151
145
|
return value;
|
|
152
146
|
}
|
|
153
147
|
/**
|
|
154
|
-
*
|
|
155
|
-
* @memberof GlobalState
|
|
156
|
-
* @desc Unsubscribes `callback` from watching state updates; no operation if
|
|
148
|
+
* Unsubscribes `callback` from watching state updates; no operation if
|
|
157
149
|
* `callback` is not subscribed to the state updates.
|
|
158
150
|
* @param {function} callback
|
|
159
151
|
* @throws if {@link SsrContext} is attached to the state instance: the state
|
|
@@ -173,9 +165,7 @@ var GlobalState = /*#__PURE__*/function () {
|
|
|
173
165
|
}
|
|
174
166
|
}
|
|
175
167
|
/**
|
|
176
|
-
*
|
|
177
|
-
* @memberof GlobalState
|
|
178
|
-
* @desc Subscribes `callback` to watch state updates; no operation if
|
|
168
|
+
* Subscribes `callback` to watch state updates; no operation if
|
|
179
169
|
* `callback` is already subscribed to this state instance.
|
|
180
170
|
* @param {function} callback It will be called without any arguments every
|
|
181
171
|
* time the state content changes (note, howhever, separate state updates can
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/GlobalState.js"],"names":["ERR_NO_SSR_WATCH","fullPath","statePath","GlobalState","initialState","ssrContext","state","nextNotifierId","watchers","dirty","pending","process","env","NODE_ENV","msg","console","groupCollapsed","log","groupEnd","path","value","p","pos","pathSegments","i","length","seg","next","setTimeout","forEach","w","callback","Error","indexOf","pop","push"],"mappings":";;;;;;;;;;;;;;;;;AAAA;;AAUA;;;;;;AAEA,IAAMA,gBAAgB,GAAG,gDAAzB;AAEA;AACA;AACA;AACA;AACA;AACA;;AACA,SAASC,QAAT,CAAkBC,SAAlB,EAA6B;AAC3B,SAAO,mBAAMA,SAAN,IAAmB,OAAnB,mBAAsCA,SAAtC,CAAP;AACD;;IAEoBC,W;AACnB;AACF;AACA;AACA;AACA;
|
|
1
|
+
{"version":3,"sources":["../../src/GlobalState.js"],"names":["ERR_NO_SSR_WATCH","fullPath","statePath","GlobalState","initialState","ssrContext","state","nextNotifierId","watchers","dirty","pending","process","env","NODE_ENV","msg","console","groupCollapsed","log","groupEnd","path","value","p","pos","pathSegments","i","length","seg","next","setTimeout","forEach","w","callback","Error","indexOf","pop","push"],"mappings":"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","sourcesContent":["import {\n cloneDeep,\n get,\n isArray,\n isObject,\n isNil,\n set,\n toPath,\n} from 'lodash';\n\nimport { isDebugMode } from './utils';\n\nconst ERR_NO_SSR_WATCH = 'GlobalState must not be watched at server side';\n\n/**\n * Transform state path into the full path inside GlobalState object.\n * @param {string} statePath\n * @return {string}\n * @ignore\n */\nfunction fullPath(statePath) {\n return isNil(statePath) ? 'state' : `state.${statePath}`;\n}\n\nexport default class GlobalState {\n /**\n * Creates a new global state object.\n * @param {any} [initialState] Intial global state content.\n * @param {SsrContext} [ssrContext] Server-side rendering context.\n */\n constructor(initialState, ssrContext) {\n /* eslint-disable no-param-reassign */\n this.state = cloneDeep(initialState);\n this.nextNotifierId = null;\n this.watchers = [];\n\n if (ssrContext) {\n ssrContext.dirty = false;\n ssrContext.pending = [];\n ssrContext.state = this.state;\n this.ssrContext = ssrContext;\n }\n\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n let msg = 'New ReactGlobalState created';\n if (ssrContext) msg += ' (SSR mode)';\n console.groupCollapsed(msg);\n console.log('Initial state:', cloneDeep(initialState));\n console.groupEnd();\n /* eslint-enable no-console */\n }\n /* eslint-enable no-param-reassign */\n }\n\n /**\n * Gets the value at given `path` of global state. If `path` is null or\n * undefined, the entire state object is returned.\n * @param {string} [path] Dot-delimitered state path. If not given, entire\n * global state content is returned.\n * @return {any}\n */\n get(path) {\n return get(this, fullPath(path));\n }\n\n /**\n * Writes the `value` to given global state `path`.\n * @param {string} [path] Dot-delimitered state path. If not given, entire\n * global state content is replaced by the `value`.\n * @param {any} value The value.\n * @return {any} Given `value` itself.\n */\n set(path, value) {\n const p = fullPath(path);\n if (value !== get(this, p)) {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupCollapsed(\n `ReactGlobalState update. Path: \"${path || ''}\"`,\n );\n console.log('New value:', cloneDeep(value));\n /* eslint-enable no-console */\n }\n let pos = this;\n const pathSegments = toPath(p);\n for (let i = 0; i < pathSegments.length - 1; i += 1) {\n const seg = pathSegments[i];\n const next = pos[seg];\n if (isArray(next)) pos[seg] = [...next];\n else if (isObject(next)) pos[seg] = { ...next };\n else break;\n pos = pos[seg];\n }\n\n // TODO: With such naive use of _.set, the state is mutated in place,\n // which may cause tons of unexpected side effects for dependants.\n // It will be better to partially clone the state, so that any existing\n // references are not mutated, while the full deep clonning is also\n // avoided.\n set(this, p, value);\n\n if (this.ssrContext) {\n this.ssrContext.dirty = true;\n this.ssrContext.state = this.state;\n } else if (!this.nextNotifierId) {\n this.nextNotifierId = setTimeout(() => {\n this.nextNotifierId = null;\n [...this.watchers].forEach((w) => w());\n });\n }\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.log('New state:', cloneDeep(this.state));\n console.groupEnd();\n /* eslint-enable no-console */\n }\n }\n return value;\n }\n\n /**\n * Unsubscribes `callback` from watching state updates; no operation if\n * `callback` is not subscribed to the state updates.\n * @param {function} callback\n * @throws if {@link SsrContext} is attached to the state instance: the state\n * watching functionality is intended for client-side (non-SSR) only.\n */\n unWatch(callback) {\n if (this.ssrContext) throw new Error(ERR_NO_SSR_WATCH);\n const { watchers } = this;\n const pos = watchers.indexOf(callback);\n if (pos >= 0) {\n watchers[pos] = watchers[watchers.length - 1];\n watchers.pop();\n }\n }\n\n /**\n * Subscribes `callback` to watch state updates; no operation if\n * `callback` is already subscribed to this state instance.\n * @param {function} callback It will be called without any arguments every\n * time the state content changes (note, howhever, separate state updates can\n * be applied to the state at once, and watching callbacks will be called once\n * after such bulk update).\n * @throws if {@link SsrContext} is attached to the state instance: the state\n * watching functionality is intended for client-side (non-SSR) only.\n */\n watch(callback) {\n if (this.ssrContext) throw new Error(ERR_NO_SSR_WATCH);\n const { watchers } = this;\n if (watchers.indexOf(callback) < 0) {\n watchers.push(callback);\n }\n }\n}\n"],"file":"GlobalState.js"}
|
|
@@ -18,27 +18,9 @@ var _GlobalState = _interopRequireDefault(require("./GlobalState"));
|
|
|
18
18
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
19
19
|
|
|
20
20
|
/* eslint-disable react/prop-types */
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @typedef {object} SsrContext Holds global-state-related information,
|
|
24
|
-
* which should be persistent across rendering iterations during server-side
|
|
25
|
-
* rendering (SSR). For the first SSR iteration any object, including an empty
|
|
26
|
-
* `{}`, may be provided to {@link <GlobalStateProvider>}: in either case
|
|
27
|
-
* all its fields listed below will be (re-)initialized as needed, and any other
|
|
28
|
-
* fields contained in the object won't be touched by the library (thus, you may
|
|
29
|
-
* use it to keep other data you need across SSR iterations, and you can access
|
|
30
|
-
* it from React components via {@link getSsrContext} hook).
|
|
31
|
-
* @prop {boolean} dirty `true` if the global state has been modified in
|
|
32
|
-
* the last SSR iteration; `false` otherwise.
|
|
33
|
-
* @prop {Promise[]} pending An array of promises waiting for completion of
|
|
34
|
-
* asynchronous state operations (like {@link useAsyncData}), initiated during
|
|
35
|
-
* the last SSR iteration.
|
|
36
|
-
* @prop {any} state The global state content at the end of last SSR iteration.
|
|
37
|
-
*/
|
|
38
21
|
var context = /*#__PURE__*/(0, _react.createContext)();
|
|
39
22
|
/**
|
|
40
|
-
* @
|
|
41
|
-
* @desc Gets {@link GlobalState} instance from the context. In most cases
|
|
23
|
+
* Gets {@link GlobalState} instance from the context. In most cases
|
|
42
24
|
* you should use {@link useGlobalState}, and other hooks to interact with
|
|
43
25
|
* the global state, instead of accessing it directly.
|
|
44
26
|
* @return {GlobalState}
|
|
@@ -87,9 +69,7 @@ function getSsrContext() {
|
|
|
87
69
|
return ssrContext;
|
|
88
70
|
}
|
|
89
71
|
/**
|
|
90
|
-
*
|
|
91
|
-
* @name <GlobalStateProvider>
|
|
92
|
-
* @desc Provides global state to its children.
|
|
72
|
+
* Provides global state to its children.
|
|
93
73
|
* @prop {ReactNode} [children] Component children, which will be provided with
|
|
94
74
|
* the global state, and rendered in place of the provider.
|
|
95
75
|
* @prop {any} [initialState] Initial content of the global state.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/GlobalStateProvider.jsx"],"names":["context","getGlobalState","globalState","Error","getSsrContext","throwWithoutSsrContext","ssrContext","GlobalStateProvider","children","initialState","stateProxy","state","GlobalState"],"mappings":";;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../../src/GlobalStateProvider.jsx"],"names":["context","getGlobalState","globalState","Error","getSsrContext","throwWithoutSsrContext","ssrContext","GlobalStateProvider","children","initialState","stateProxy","state","GlobalState"],"mappings":";;;;;;;;;;;;;AAEA;;AAEA;;;;AAJA;AAMA,IAAMA,OAAO,gBAAG,2BAAhB;AAEA;AACA;AACA;AACA;AACA;AACA;;AACO,SAASC,cAAT,GAA0B;AAC/B;AACA;AACA;AACA;AACA;;AACA;AACA,MAAMC,WAAW,GAAG,uBAAWF,OAAX,CAApB;AACA;;AACA,MAAI,CAACE,WAAL,EAAkB,MAAM,IAAIC,KAAJ,CAAU,6BAAV,CAAN;AAClB,SAAOD,WAAP;AACD;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACO,SAASE,aAAT,GAAsD;AAAA,MAA/BC,sBAA+B,uEAAN,IAAM;;AAC3D,wBAAuBJ,cAAc,EAArC;AAAA,MAAQK,UAAR,mBAAQA,UAAR;;AACA,MAAI,CAACA,UAAD,IAAeD,sBAAnB,EAA2C;AACzC,UAAM,IAAIF,KAAJ,CAAU,sBAAV,CAAN;AACD;;AACD,SAAOG,UAAP;AACD;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACe,SAASC,mBAAT,OAKZ;AAAA,MAJDC,QAIC,QAJDA,QAIC;AAAA,MAHDC,YAGC,QAHDA,YAGC;AAAA,MAFDH,UAEC,QAFDA,UAEC;AAAA,MADDI,UACC,QADDA,UACC;AACD,MAAIC,KAAJ,CADC,CAED;AACA;AACA;AACA;;AACA;;AACA,MAAID,UAAU,YAAYE,oBAA1B,EAAuCD,KAAK,GAAGD,UAAR,CAAvC,KACK,IAAIA,UAAJ,EAAgBC,KAAK,GAAGV,cAAc,EAAtB,CAAhB;AAAA,oBACU,qBAAS,IAAIW,oBAAJ,CAAgBH,YAAhB,EAA8BH,UAA9B,CAAT,CADV;;AAAA;;AACCK,IAAAA,KADD;AAAA;AAEL;;AACA,sBACE,qBAAC,OAAD,CAAS,QAAT;AAAkB,IAAA,KAAK,EAAEA,KAAzB;AAAA,cACGH;AADH,IADF;AAKD","sourcesContent":["/* eslint-disable react/prop-types */\n\nimport { createContext, useContext, useState } from 'react';\n\nimport GlobalState from './GlobalState';\n\nconst context = createContext();\n\n/**\n * Gets {@link GlobalState} instance from the context. In most cases\n * you should use {@link useGlobalState}, and other hooks to interact with\n * the global state, instead of accessing it directly.\n * @return {GlobalState}\n */\nexport function getGlobalState() {\n // Here Rules of Hooks are violated because \"getGlobalState()\" does not follow\n // convention that hook names should start with use... This is intentional in\n // our case, as getGlobalState() hook is intended for advance scenarious,\n // while the normal interaction with the global state should happen via\n // another hook, useGlobalState().\n /* eslint-disable react-hooks/rules-of-hooks */\n const globalState = useContext(context);\n /* eslint-enable react-hooks/rules-of-hooks */\n if (!globalState) throw new Error('Missing GlobalStateProvider');\n return globalState;\n}\n\n/**\n * @category Hooks\n * @desc Gets SSR context.\n * @param {boolean} [throwWithoutSsrContext=true] If `true` (default),\n * this hook will throw if no SSR context is attached to the global state;\n * set `false` to not throw in such case. In either case the hook will throw\n * if the {@link <GlobalStateProvider>} (hence the state) is missing.\n * @returns {SsrContext} SSR context.\n * @throws\n * - If current component has no parent {@link <GlobalStateProvider>}\n * in the rendered React tree.\n * - If `throwWithoutSsrContext` is `true`, and there is no SSR context attached\n * to the global state provided by {@link <GlobalStateProvider>}.\n */\nexport function getSsrContext(throwWithoutSsrContext = true) {\n const { ssrContext } = getGlobalState();\n if (!ssrContext && throwWithoutSsrContext) {\n throw new Error('No SSR context found');\n }\n return ssrContext;\n}\n\n/**\n * Provides global state to its children.\n * @prop {ReactNode} [children] Component children, which will be provided with\n * the global state, and rendered in place of the provider.\n * @prop {any} [initialState] Initial content of the global state.\n * @prop {SsrContext} [ssrContext] Server-side rendering (SSR) context.\n * @prop {boolean|GlobalState} [stateProxy] This option is useful for code\n * splitting and SSR implementation:\n * - If `true`, this provider instance will fetch and reuse the global state\n * from a parent provider.\n * - If `GlobalState` instance, it will be used by this provider.\n * - If not given, a new `GlobalState` instance will be created and used.\n */\nexport default function GlobalStateProvider({\n children,\n initialState,\n ssrContext,\n stateProxy,\n}) {\n let state;\n // Here Rules of Hooks are violated because hooks are called conditionally,\n // however we assume that these properties should not change at runtime, thus\n // the actual hook order is preserved. Probably, it should be handled better,\n // though.\n /* eslint-disable react-hooks/rules-of-hooks */\n if (stateProxy instanceof GlobalState) state = stateProxy;\n else if (stateProxy) state = getGlobalState();\n else [state] = useState(new GlobalState(initialState, ssrContext));\n /* eslint-enable react-hooks/rules-of-hooks */\n return (\n <context.Provider value={state}>\n {children}\n </context.Provider>\n );\n}\n"],"file":"GlobalStateProvider.js"}
|
|
@@ -14,20 +14,7 @@ var _useAsyncData = _interopRequireDefault(require("./useAsyncData"));
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
* `loader` function accepted by {@link useAsyncCollection} hook.
|
|
19
|
-
* @param {string} id ID of the collection item to load.
|
|
20
|
-
* @param {any} oldData Previously fetched data for this ID, if any. The loader
|
|
21
|
-
* does not have to use it, it is provided just for convenience, when the newly
|
|
22
|
-
* resolved data may depend on the previously fetched data.
|
|
23
|
-
* @returns {Promise<any>} Resolves to data to be stored in the global state
|
|
24
|
-
* for the given collection item ID.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @category Hooks
|
|
29
|
-
* @func useAsyncCollection
|
|
30
|
-
* @desc Resolves and stores at the given `path` of global state elements of
|
|
17
|
+
* Resolves and stores at the given `path` of global state elements of
|
|
31
18
|
* an asynchronous data collection. In other words, it is an auxiliar wrapper
|
|
32
19
|
* around {@link useAsyncData}, which uses a loader which resolves to different
|
|
33
20
|
* data, based on ID argument passed in, and stores data fetched for different
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/useAsyncCollection.js"],"names":["useAsyncCollection","id","path","loader","options","itemPath","oldData"],"mappings":";;;;;;;;;AAIA;;AAJA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA
|
|
1
|
+
{"version":3,"sources":["../../src/useAsyncCollection.js"],"names":["useAsyncCollection","id","path","loader","options","itemPath","oldData"],"mappings":";;;;;;;;;AAIA;;AAJA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAASA,kBAAT,CACbC,EADa,EAEbC,IAFa,EAGbC,MAHa,EAKb;AAAA,MADAC,OACA,uEADU,EACV;AACA,MAAMC,QAAQ,GAAGH,IAAI,aAAMA,IAAN,cAAcD,EAAd,IAAqBA,EAA1C;AACA,SAAO,2BAAaI,QAAb,EAAuB,UAACC,OAAD;AAAA,WAAaH,MAAM,CAACF,EAAD,EAAKK,OAAL,CAAnB;AAAA,GAAvB,EAAyDF,OAAzD,CAAP;AACD","sourcesContent":["/**\n * Loads and uses an item in an async collection.\n */\n\nimport useAsyncData from './useAsyncData';\n\n/**\n * Resolves and stores at the given `path` of global state elements of\n * an asynchronous data collection. In other words, it is an auxiliar wrapper\n * around {@link useAsyncData}, which uses a loader which resolves to different\n * data, based on ID argument passed in, and stores data fetched for different\n * IDs in the state.\n * @param {string} id ID of the collection item to load & use.\n * @param {string} path The global state path where entire collection should be\n * stored.\n * @param {AsyncCollectionLoader} loader A loader function, which takes an\n * ID of data to load, and resolves to the corresponding data.\n * @param {object} [options] Additional options.\n * @param {any[]} [options.deps=[]] An array of dependencies, which trigger\n * data reload when changed. Given dependency changes are watched shallowly\n * (similarly to the standard React's\n * [useEffect()](https://reactjs.org/docs/hooks-reference.html#useeffect)).\n * @param {boolean} [options.noSSR] If `true`, this hook won't load data during\n * server-side rendering.\n * @param {number} [options.garbageCollectAge=maxage] The maximum age of data\n * (in milliseconds), after which they are dropped from the state when the last\n * component referencing them via `useAsyncData()` hook unmounts. Defaults to\n * `maxage` option value.\n * @param {number} [options.maxage=5 x 60 x 1000] The maximum age of\n * data (in milliseconds) acceptable to the hook's caller. If loaded data are\n * older than this value, `null` is returned instead. Defaults to 5 minutes.\n * @param {number} [options.refreshAge=maxage] The maximum age of data\n * (in milliseconds), after which their refreshment will be triggered when\n * any component referencing them via `useAsyncData()` hook (re-)renders.\n * Defaults to `maxage` value.\n * @return {{\n * data: any,\n * loading: boolean,\n * timestamp: number\n * }} Returns an object with three fields: `data` holds the actual result of\n * last `loader` invokation, if any, and if satisfies `maxage` limit; `loading`\n * is a boolean flag, which is `true` if data are being loaded (the hook is\n * waiting for `loader` function resolution); `timestamp` (in milliseconds)\n * is Unix timestamp of related data currently loaded into the global state.\n *\n * Note that loaded data, if any, are stored at the given `path` of global state\n * along with related meta-information, using slightly different state segment\n * structure (see {@link AsyncDataEnvelope}). That segment of the global state\n * can be accessed, and even modified using other hooks,\n * _e.g._ {@link useGlobalState}, but doing so you may interfere with related\n * `useAsyncData()` hooks logic.\n */\nexport default function useAsyncCollection(\n id,\n path,\n loader,\n options = {},\n) {\n const itemPath = path ? `${path}.${id}` : id;\n return useAsyncData(itemPath, (oldData) => loader(id, oldData), options);\n}\n"],"file":"useAsyncCollection.js"}
|
|
@@ -27,9 +27,9 @@ var _useGlobalState3 = _interopRequireDefault(require("./useGlobalState"));
|
|
|
27
27
|
|
|
28
28
|
var _utils = require("./utils");
|
|
29
29
|
|
|
30
|
-
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object);
|
|
30
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
31
31
|
|
|
32
|
-
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]
|
|
32
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
33
33
|
|
|
34
34
|
var DEFAULT_MAXAGE = 5 * 60 * 1000; // 5 minutes.
|
|
35
35
|
|
|
@@ -52,40 +52,7 @@ function load(_x, _x2, _x3, _x4) {
|
|
|
52
52
|
return _load.apply(this, arguments);
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
56
|
-
* global state segment, created by {@link useAsyncData} hook for storing of
|
|
57
|
-
* loaded async data, and associated metadata.
|
|
58
|
-
* @prop {any} data The actual loaded data.
|
|
59
|
-
* @prop {number} numRefs The count of currently mounted components referencing
|
|
60
|
-
* the data via `useAsyncData` hooks.
|
|
61
|
-
* @prop {string} operationId A unique ID of the current data loading
|
|
62
|
-
* operation, if one is in progress. Changing this ID before the operation
|
|
63
|
-
* completes effectively cancels the ongoing operation, and instructs related
|
|
64
|
-
* hook to ignore the operation result.
|
|
65
|
-
*
|
|
66
|
-
* NOTE: Server-side and client-side operation UIDs start with `S` or `C` letter
|
|
67
|
-
* respectively. At the client side, if an envelop stores `operationId` starting
|
|
68
|
-
* with `S` letter, it is understood as a non-terminated data loading operation
|
|
69
|
-
* during SSR, and it is restarted at the client-side in this case.
|
|
70
|
-
* @prop {number} timestamp Unix timestamp (in milliseconds) of the most
|
|
71
|
-
* recently loaded `data`.
|
|
72
|
-
*/
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* @typedef {function} AsyncDataLoader This type documents the signature of
|
|
76
|
-
* async data loader function, expected by {@link useAsyncData} hook.
|
|
77
|
-
* @param {any} oldData Previously loaded data (_i.e._ the data currently stored
|
|
78
|
-
* at the global state `path` managed by the corresponding `useAsyncData` hook,
|
|
79
|
-
* which are assumed to be resolved from a previous call to the loader).
|
|
80
|
-
* The loader does not have to use this argument, it is provided just for
|
|
81
|
-
* convenience.
|
|
82
|
-
* @return {Promise<any>} Async data to store to the state.
|
|
83
|
-
*/
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* @category Hooks
|
|
87
|
-
* @func useAsyncData
|
|
88
|
-
* @desc Resolves asynchronous data, and stores them at given `path` of global
|
|
55
|
+
* Resolves asynchronous data, and stores them at given `path` of global
|
|
89
56
|
* state. When multiple components rely on asynchronous data at the same `path`,
|
|
90
57
|
* the data are resolved once, and reused until their age is within specified
|
|
91
58
|
* bounds. Once the data are stale, the hook allows to refresh them. It also
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/useAsyncData.js"],"names":["DEFAULT_MAXAGE","load","path","loader","globalState","oldData","opIdPrefix","process","env","NODE_ENV","console","log","operationId","operationIdPath","set","get","data","state","groupCollapsed","timestamp","Date","now","groupEnd","useAsyncData","options","garbageCollectAge","maxage","refreshAge","undefined","numRefs","localState","ssrContext","noSSR","pending","push","numRefsPath","loadTriggered","charAt","deps","length","loading","Boolean"],"mappings":";;;;;;;;;;;;;;;;;AAIA;;AACA;;AACA;;AAEA;;AACA;;AACA;;;;;;AAEA,IAAMA,cAAc,GAAG,IAAI,EAAJ,GAAS,IAAhC,C,CAAsC;;AAEtC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;SACeC,I;;;AAsCf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;kFAzHA,iBAAoBC,IAApB,EAA0BC,MAA1B,EAAkCC,WAAlC,EAA+CC,OAA/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAwDC,YAAAA,UAAxD,2DAAqE,GAArE;;AACE,gBAAIC,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IAAyC,yBAA7C,EAA4D;AAC1D;AACAC,cAAAA,OAAO,CAACC,GAAR,qEAC8DT,IAAI,IAAI,EADtE;AAGA;AACD;;AACKU,YAAAA,WARR,GAQsBN,UAAU,GAAG,eARnC;AASQO,YAAAA,eATR,GAS0BX,IAAI,aAAMA,IAAN,oBAA2B,aATzD;AAUEE,YAAAA,WAAW,CAACU,GAAZ,CAAgBD,eAAhB,EAAiCD,WAAjC;AAVF;AAAA,mBAWqBT,MAAM,CAACE,OAAO,IAAID,WAAW,CAACW,GAAZ,CAAgBb,IAAhB,EAAsBc,IAAlC,CAX3B;;AAAA;AAWQA,YAAAA,IAXR;AAYQC,YAAAA,KAZR,GAYgBb,WAAW,CAACW,GAAZ,CAAgBb,IAAhB,C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","sourcesContent":["/**\n * Loads and uses async data into the GlobalState path.\n */\n\nimport { cloneDeep } from 'lodash';\nimport { useEffect } from 'react';\nimport { v4 as uuid } from 'uuid';\n\nimport { getGlobalState } from './GlobalStateProvider';\nimport useGlobalState from './useGlobalState';\nimport { isDebugMode } from './utils';\n\nconst DEFAULT_MAXAGE = 5 * 60 * 1000; // 5 minutes.\n\n/**\n * Executes the data loading operation.\n * @param {string} path Data segment path inside the global state.\n * @param {function} loader Data loader.\n * @param {GlobalState} globalState The global state instance.\n * @param {any} [oldData] Optional. Previously fetched data, currently stored in\n * the state, if already fetched by the caller; otherwise, they will be fetched\n * by the load() function itself.\n * @param {string} [opIdPrefix='C'] operationId prefix to use, which should be\n * 'C' at the client-side (default), or 'S' at the server-side (within SSR\n * context).\n * @return {Promise} Resolves once the operation is done.\n * @ignore\n */\nasync function load(path, loader, globalState, oldData, opIdPrefix = 'C') {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.log(\n `ReactGlobalState: useAsyncData data (re-)loading. Path: \"${path || ''}\"`,\n );\n /* eslint-enable no-console */\n }\n const operationId = opIdPrefix + uuid();\n const operationIdPath = path ? `${path}.operationId` : 'operationId';\n globalState.set(operationIdPath, operationId);\n const data = await loader(oldData || globalState.get(path).data);\n const state = globalState.get(path);\n if (operationId === state.operationId) {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupCollapsed(\n `ReactGlobalState: useAsyncData data (re-)loaded. Path: \"${\n path || ''\n }\"`,\n );\n console.log('Data:', cloneDeep(data));\n /* eslint-enable no-console */\n }\n globalState.set(path, {\n ...state,\n data,\n operationId: '',\n timestamp: Date.now(),\n });\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupEnd();\n /* eslint-enable no-console */\n }\n }\n}\n\n/**\n * @typedef {object} AsyncDataEnvelope This type documents the structure of\n * global state segment, created by {@link useAsyncData} hook for storing of\n * loaded async data, and associated metadata.\n * @prop {any} data The actual loaded data.\n * @prop {number} numRefs The count of currently mounted components referencing\n * the data via `useAsyncData` hooks.\n * @prop {string} operationId A unique ID of the current data loading\n * operation, if one is in progress. Changing this ID before the operation\n * completes effectively cancels the ongoing operation, and instructs related\n * hook to ignore the operation result.\n *\n * NOTE: Server-side and client-side operation UIDs start with `S` or `C` letter\n * respectively. At the client side, if an envelop stores `operationId` starting\n * with `S` letter, it is understood as a non-terminated data loading operation\n * during SSR, and it is restarted at the client-side in this case.\n * @prop {number} timestamp Unix timestamp (in milliseconds) of the most\n * recently loaded `data`.\n */\n\n/**\n * @typedef {function} AsyncDataLoader This type documents the signature of\n * async data loader function, expected by {@link useAsyncData} hook.\n * @param {any} oldData Previously loaded data (_i.e._ the data currently stored\n * at the global state `path` managed by the corresponding `useAsyncData` hook,\n * which are assumed to be resolved from a previous call to the loader).\n * The loader does not have to use this argument, it is provided just for\n * convenience.\n * @return {Promise<any>} Async data to store to the state.\n */\n\n/**\n * @category Hooks\n * @func useAsyncData\n * @desc Resolves asynchronous data, and stores them at given `path` of global\n * state. When multiple components rely on asynchronous data at the same `path`,\n * the data are resolved once, and reused until their age is within specified\n * bounds. Once the data are stale, the hook allows to refresh them. It also\n * garbage-collects stale data from the global state when the last component\n * relying on them is unmounted.\n * @param {string} path Dot-delimitered state path, where data envelop is\n * stored.\n * @param {AsyncDataLoader} loader Asynchronous function which resolves (loads)\n * data, which should be stored at the global state `path`. When multiple\n * components\n * use `useAsyncData()` hook for the same `path`, the library assumes that all\n * hook instances are called with the same `loader` (_i.e._ whichever of these\n * loaders is used to resolve async data, the result is acceptable to be reused\n * in all related components).\n * @param {object} [options] Additional options.\n * @param {any[]} [options.deps=[]] An array of dependencies, which trigger\n * data reload when changed. Given dependency changes are watched shallowly\n * (similarly to the standard React's\n * [useEffect()](https://reactjs.org/docs/hooks-reference.html#useeffect)).\n * @param {boolean} [options.noSSR] If `true`, this hook won't load data during\n * server-side rendering.\n * @param {number} [options.garbageCollectAge=maxage] The maximum age of data\n * (in milliseconds), after which they are dropped from the state when the last\n * component referencing them via `useAsyncData()` hook unmounts. Defaults to\n * `maxage` option value.\n * @param {number} [options.maxage=5 x 60 x 1000] The maximum age of\n * data (in milliseconds) acceptable to the hook's caller. If loaded data are\n * older than this value, `null` is returned instead. Defaults to 5 minutes.\n * @param {number} [options.refreshAge=maxage] The maximum age of data\n * (in milliseconds), after which their refreshment will be triggered when\n * any component referencing them via `useAsyncData()` hook (re-)renders.\n * Defaults to `maxage` value.\n * @return {{\n * data: any,\n * loading: boolean,\n * timestamp: number\n * }} Returns an object with three fields: `data` holds the actual result of\n * last `loader` invokation, if any, and if satisfies `maxage` limit; `loading`\n * is a boolean flag, which is `true` if data are being loaded (the hook is\n * waiting for `loader` function resolution); `timestamp` (in milliseconds)\n * is Unix timestamp of related data currently loaded into the global state.\n *\n * Note that loaded data, if any, are stored at the given `path` of global state\n * along with related meta-information, using slightly different state segment\n * structure (see {@link AsyncDataEnvelope}). That segment of the global state\n * can be accessed, and even modified using other hooks,\n * _e.g._ {@link useGlobalState}, but doing so you may interfere with related\n * `useAsyncData()` hooks logic.\n */\nexport default function useAsyncData(\n path,\n loader,\n options = {},\n) {\n let { garbageCollectAge, maxage, refreshAge } = options;\n if (maxage === undefined) maxage = DEFAULT_MAXAGE;\n if (refreshAge === undefined) refreshAge = maxage;\n if (garbageCollectAge === undefined) garbageCollectAge = maxage;\n\n const globalState = getGlobalState();\n const [localState] = useGlobalState(path, {\n data: null,\n numRefs: 0,\n operationId: '',\n timestamp: 0,\n });\n\n if (globalState.ssrContext && !options.noSSR) {\n const state = globalState.get(path);\n if (!state.timestamp && !state.operationId) {\n globalState.ssrContext.pending.push(\n load(path, loader, globalState, state.data, 'S'),\n );\n }\n } else {\n // This takes care about the client-side reference counting, and garbage\n // collection.\n //\n // Note: the Rules of Hook below are violated by conditional call to a hook,\n // but as the condition is actually server-side or client-side environment,\n // it is effectively non-conditional at the runtime.\n //\n // TODO: Though, maybe there is a way to refactor it into a cleaner code.\n // The same applies to other useEffect() hooks below.\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n const numRefsPath = path ? `${path}.numRefs` : 'numRefs';\n const numRefs = globalState.get(numRefsPath);\n globalState.set(numRefsPath, numRefs + 1);\n return () => {\n const state = globalState.get(path);\n if (\n state.numRefs === 1\n && garbageCollectAge < Date.now() - state.timestamp\n ) {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.log(\n `ReactGlobalState - useAsyncData garbage collected at path ${\n path || ''\n }`,\n );\n /* eslint-enable no-console */\n }\n globalState.set(path, {\n ...state,\n data: null,\n numRefs: 0,\n timestamp: 0,\n });\n } else globalState.set(numRefsPath, state.numRefs - 1);\n };\n }, [garbageCollectAge, globalState, path]);\n\n // Note: a bunch of Rules of Hooks ignored belows because in our very\n // special case the otherwise wrong behavior is actually what we need.\n\n // Data loading and refreshing.\n let loadTriggered = false;\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n const state = globalState.get(path);\n if (refreshAge < Date.now() - state.timestamp\n && (!state.operationId || state.operationId.charAt() === 'S')) {\n load(path, loader, globalState, state.data);\n loadTriggered = true; // eslint-disable-line react-hooks/exhaustive-deps\n }\n });\n\n const deps = options.deps || [];\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n if (!loadTriggered && deps.length) load(path, loader, globalState);\n }, deps); // eslint-disable-line react-hooks/exhaustive-deps\n }\n\n return {\n data: maxage < Date.now() - localState.timestamp ? null : localState.data,\n loading: Boolean(localState.operationId),\n timestamp: localState.timestamp,\n };\n}\n"],"file":"useAsyncData.js"}
|
|
1
|
+
{"version":3,"sources":["../../src/useAsyncData.js"],"names":["DEFAULT_MAXAGE","load","path","loader","globalState","oldData","opIdPrefix","process","env","NODE_ENV","console","log","operationId","operationIdPath","set","get","data","state","groupCollapsed","timestamp","Date","now","groupEnd","useAsyncData","options","garbageCollectAge","maxage","refreshAge","undefined","numRefs","localState","ssrContext","noSSR","pending","push","numRefsPath","loadTriggered","charAt","deps","length","loading","Boolean"],"mappings":"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","sourcesContent":["/**\n * Loads and uses async data into the GlobalState path.\n */\n\nimport { cloneDeep } from 'lodash';\nimport { useEffect } from 'react';\nimport { v4 as uuid } from 'uuid';\n\nimport { getGlobalState } from './GlobalStateProvider';\nimport useGlobalState from './useGlobalState';\nimport { isDebugMode } from './utils';\n\nconst DEFAULT_MAXAGE = 5 * 60 * 1000; // 5 minutes.\n\n/**\n * Executes the data loading operation.\n * @param {string} path Data segment path inside the global state.\n * @param {function} loader Data loader.\n * @param {GlobalState} globalState The global state instance.\n * @param {any} [oldData] Optional. Previously fetched data, currently stored in\n * the state, if already fetched by the caller; otherwise, they will be fetched\n * by the load() function itself.\n * @param {string} [opIdPrefix='C'] operationId prefix to use, which should be\n * 'C' at the client-side (default), or 'S' at the server-side (within SSR\n * context).\n * @return {Promise} Resolves once the operation is done.\n * @ignore\n */\nasync function load(path, loader, globalState, oldData, opIdPrefix = 'C') {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.log(\n `ReactGlobalState: useAsyncData data (re-)loading. Path: \"${path || ''}\"`,\n );\n /* eslint-enable no-console */\n }\n const operationId = opIdPrefix + uuid();\n const operationIdPath = path ? `${path}.operationId` : 'operationId';\n globalState.set(operationIdPath, operationId);\n const data = await loader(oldData || globalState.get(path).data);\n const state = globalState.get(path);\n if (operationId === state.operationId) {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupCollapsed(\n `ReactGlobalState: useAsyncData data (re-)loaded. Path: \"${\n path || ''\n }\"`,\n );\n console.log('Data:', cloneDeep(data));\n /* eslint-enable no-console */\n }\n globalState.set(path, {\n ...state,\n data,\n operationId: '',\n timestamp: Date.now(),\n });\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupEnd();\n /* eslint-enable no-console */\n }\n }\n}\n\n/**\n * Resolves asynchronous data, and stores them at given `path` of global\n * state. When multiple components rely on asynchronous data at the same `path`,\n * the data are resolved once, and reused until their age is within specified\n * bounds. Once the data are stale, the hook allows to refresh them. It also\n * garbage-collects stale data from the global state when the last component\n * relying on them is unmounted.\n * @param {string} path Dot-delimitered state path, where data envelop is\n * stored.\n * @param {AsyncDataLoader} loader Asynchronous function which resolves (loads)\n * data, which should be stored at the global state `path`. When multiple\n * components\n * use `useAsyncData()` hook for the same `path`, the library assumes that all\n * hook instances are called with the same `loader` (_i.e._ whichever of these\n * loaders is used to resolve async data, the result is acceptable to be reused\n * in all related components).\n * @param {object} [options] Additional options.\n * @param {any[]} [options.deps=[]] An array of dependencies, which trigger\n * data reload when changed. Given dependency changes are watched shallowly\n * (similarly to the standard React's\n * [useEffect()](https://reactjs.org/docs/hooks-reference.html#useeffect)).\n * @param {boolean} [options.noSSR] If `true`, this hook won't load data during\n * server-side rendering.\n * @param {number} [options.garbageCollectAge=maxage] The maximum age of data\n * (in milliseconds), after which they are dropped from the state when the last\n * component referencing them via `useAsyncData()` hook unmounts. Defaults to\n * `maxage` option value.\n * @param {number} [options.maxage=5 x 60 x 1000] The maximum age of\n * data (in milliseconds) acceptable to the hook's caller. If loaded data are\n * older than this value, `null` is returned instead. Defaults to 5 minutes.\n * @param {number} [options.refreshAge=maxage] The maximum age of data\n * (in milliseconds), after which their refreshment will be triggered when\n * any component referencing them via `useAsyncData()` hook (re-)renders.\n * Defaults to `maxage` value.\n * @return {{\n * data: any,\n * loading: boolean,\n * timestamp: number\n * }} Returns an object with three fields: `data` holds the actual result of\n * last `loader` invokation, if any, and if satisfies `maxage` limit; `loading`\n * is a boolean flag, which is `true` if data are being loaded (the hook is\n * waiting for `loader` function resolution); `timestamp` (in milliseconds)\n * is Unix timestamp of related data currently loaded into the global state.\n *\n * Note that loaded data, if any, are stored at the given `path` of global state\n * along with related meta-information, using slightly different state segment\n * structure (see {@link AsyncDataEnvelope}). That segment of the global state\n * can be accessed, and even modified using other hooks,\n * _e.g._ {@link useGlobalState}, but doing so you may interfere with related\n * `useAsyncData()` hooks logic.\n */\nexport default function useAsyncData(\n path,\n loader,\n options = {},\n) {\n let { garbageCollectAge, maxage, refreshAge } = options;\n if (maxage === undefined) maxage = DEFAULT_MAXAGE;\n if (refreshAge === undefined) refreshAge = maxage;\n if (garbageCollectAge === undefined) garbageCollectAge = maxage;\n\n const globalState = getGlobalState();\n const [localState] = useGlobalState(path, {\n data: null,\n numRefs: 0,\n operationId: '',\n timestamp: 0,\n });\n\n if (globalState.ssrContext && !options.noSSR) {\n const state = globalState.get(path);\n if (!state.timestamp && !state.operationId) {\n globalState.ssrContext.pending.push(\n load(path, loader, globalState, state.data, 'S'),\n );\n }\n } else {\n // This takes care about the client-side reference counting, and garbage\n // collection.\n //\n // Note: the Rules of Hook below are violated by conditional call to a hook,\n // but as the condition is actually server-side or client-side environment,\n // it is effectively non-conditional at the runtime.\n //\n // TODO: Though, maybe there is a way to refactor it into a cleaner code.\n // The same applies to other useEffect() hooks below.\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n const numRefsPath = path ? `${path}.numRefs` : 'numRefs';\n const numRefs = globalState.get(numRefsPath);\n globalState.set(numRefsPath, numRefs + 1);\n return () => {\n const state = globalState.get(path);\n if (\n state.numRefs === 1\n && garbageCollectAge < Date.now() - state.timestamp\n ) {\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.log(\n `ReactGlobalState - useAsyncData garbage collected at path ${\n path || ''\n }`,\n );\n /* eslint-enable no-console */\n }\n globalState.set(path, {\n ...state,\n data: null,\n numRefs: 0,\n timestamp: 0,\n });\n } else globalState.set(numRefsPath, state.numRefs - 1);\n };\n }, [garbageCollectAge, globalState, path]);\n\n // Note: a bunch of Rules of Hooks ignored belows because in our very\n // special case the otherwise wrong behavior is actually what we need.\n\n // Data loading and refreshing.\n let loadTriggered = false;\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n const state = globalState.get(path);\n if (refreshAge < Date.now() - state.timestamp\n && (!state.operationId || state.operationId.charAt() === 'S')) {\n load(path, loader, globalState, state.data);\n loadTriggered = true; // eslint-disable-line react-hooks/exhaustive-deps\n }\n });\n\n const deps = options.deps || [];\n useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks\n if (!loadTriggered && deps.length) load(path, loader, globalState);\n }, deps); // eslint-disable-line react-hooks/exhaustive-deps\n }\n\n return {\n data: maxage < Date.now() - localState.timestamp ? null : localState.data,\n loading: Boolean(localState.operationId),\n timestamp: localState.timestamp,\n };\n}\n"],"file":"useAsyncData.js"}
|
|
@@ -20,9 +20,7 @@ var _utils = require("./utils");
|
|
|
20
20
|
// Hook for updates of global state.
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
* @func useGlobalState
|
|
25
|
-
* @desc The primary hook for interacting with the global state, modeled after
|
|
23
|
+
* The primary hook for interacting with the global state, modeled after
|
|
26
24
|
* the standard React's
|
|
27
25
|
* [useState](https://reactjs.org/docs/hooks-reference.html#usestate).
|
|
28
26
|
* It subscribes a component to a given `path` of global state, and provides
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/useGlobalState.js"],"names":["useGlobalState","path","initialValue","globalState","state","get","value","set","localState","setLocalState","callback","active","newState","watch","unWatch","ref","current","setter","newValue","process","env","NODE_ENV","console","groupCollapsed","log","groupEnd"],"mappings":";;;;;;;;;;;AAEA;;AACA;;AAEA;;AACA;;AANA;;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;
|
|
1
|
+
{"version":3,"sources":["../../src/useGlobalState.js"],"names":["useGlobalState","path","initialValue","globalState","state","get","value","set","localState","setLocalState","callback","active","newState","watch","unWatch","ref","current","setter","newValue","process","env","NODE_ENV","console","groupCollapsed","log","groupEnd"],"mappings":";;;;;;;;;;;AAEA;;AACA;;AAEA;;AACA;;AANA;;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAASA,cAAT,CAAwBC,IAAxB,EAA8BC,YAA9B,EAA4C;AACzD,MAAMC,WAAW,GAAG,0CAApB;AACA,MAAIC,KAAK,GAAGD,WAAW,CAACE,GAAZ,CAAgBJ,IAAhB,CAAZ;;AACA,MAAI,yBAAYG,KAAZ,KAAsB,CAAC,yBAAYF,YAAZ,CAA3B,EAAsD;AACpD,QAAMI,KAAK,GAAG,wBAAWJ,YAAX,IAA2BA,YAAY,EAAvC,GAA4CA,YAA1D;AACAE,IAAAA,KAAK,GAAGD,WAAW,CAACI,GAAZ,CAAgBN,IAAhB,EAAsBK,KAAtB,CAAR;AACD;;AACD,kBAGI,qBAAS;AAAA,WAAMF,KAAN;AAAA,GAAT,CAHJ;AAAA;AAAA,MACEI,UADF;AAAA,MAEEC,aAFF;;AAKA,wBAAU,YAAM;AACd;AACA;AACA;AACA;AACA,QAAMC,QAAQ,GAAG,SAAXA,QAAW,GAAM;AACrB,UAAIA,QAAQ,CAACC,MAAb,EAAqB;AACnB,YAAMC,QAAQ,GAAGT,WAAW,CAACE,GAAZ,CAAgBJ,IAAhB,CAAjB;AACA,YAAIW,QAAQ,KAAKJ,UAAjB,EAA6BC,aAAa,CAAC;AAAA,iBAAMG,QAAN;AAAA,SAAD,CAAb;AAC9B;AACF,KALD;;AAMAF,IAAAA,QAAQ,CAACC,MAAT,GAAkB,IAAlB;AACAR,IAAAA,WAAW,CAACU,KAAZ,CAAkBH,QAAlB;AACAA,IAAAA,QAAQ;AACR,WAAO,YAAM;AACX,aAAOA,QAAQ,CAACC,MAAhB;AACAR,MAAAA,WAAW,CAACW,OAAZ,CAAoBJ,QAApB;AACD,KAHD;AAID,GAlBD,EAkBG,CAACP,WAAD,EAAcK,UAAd,EAA0BP,IAA1B,CAlBH;AAoBA,MAAMc,GAAG,GAAG,oBAAZ;;AACA,MAAI,CAACA,GAAG,CAACC,OAAT,EAAkB;AAChBD,IAAAA,GAAG,CAACC,OAAJ,GAAc;AACZR,MAAAA,UAAU,EAAVA,UADY;AAEZP,MAAAA,IAAI,EAAJA,IAFY;AAGZgB,MAAAA,MAAM,EAAE,gBAACX,KAAD,EAAW;AACjB,YAAMY,QAAQ,GAAG,wBAAWZ,KAAX,IACbA,KAAK,CAACS,GAAG,CAACC,OAAJ,CAAYR,UAAb,CADQ,GACmBF,KADpC;;AAEA,YAAIa,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IAAyC,yBAA7C,EAA4D;AAC1D;AACAC,UAAAA,OAAO,CAACC,cAAR,uEAEIR,GAAG,CAACC,OAAJ,CAAYf,IAAZ,IAAoB,EAFxB;AAKAqB,UAAAA,OAAO,CAACE,GAAR,CAAY,YAAZ,EAA0B,uBAAUN,QAAV,CAA1B;AACAI,UAAAA,OAAO,CAACG,QAAR;AACA;AACD;;AAEDtB,QAAAA,WAAW,CAACI,GAAZ,CAAgBQ,GAAG,CAACC,OAAJ,CAAYf,IAA5B,EAAkCiB,QAAlC,EAfiB,CAiBjB;AACA;AACA;AACA;AACA;AACA;;AACAT,QAAAA,aAAa,CAAC;AAAA,iBAAMS,QAAN;AAAA,SAAD,CAAb;AACD;AA3BW,KAAd;AA6BD,GA9BD,MA8BO;AACLH,IAAAA,GAAG,CAACC,OAAJ,CAAYR,UAAZ,GAAyBA,UAAzB;AACAO,IAAAA,GAAG,CAACC,OAAJ,CAAYf,IAAZ,GAAmBA,IAAnB;AACD;;AAED,SAAO,CACLO,UADK,EAELO,GAAG,CAACC,OAAJ,CAAYC,MAFP,CAAP;AAID","sourcesContent":["// Hook for updates of global state.\n\nimport { cloneDeep, isFunction, isUndefined } from 'lodash';\nimport { useEffect, useRef, useState } from 'react';\n\nimport { getGlobalState } from './GlobalStateProvider';\nimport { isDebugMode } from './utils';\n\n/**\n * The primary hook for interacting with the global state, modeled after\n * the standard React's\n * [useState](https://reactjs.org/docs/hooks-reference.html#usestate).\n * It subscribes a component to a given `path` of global state, and provides\n * a function to update it. Each time the value at `path` changes, the hook\n * triggers re-render of its host component.\n *\n * **Note:**\n * - For performance, the library does not copy objects written to / read from\n * global state paths. You MUST NOT manually mutate returned state values,\n * or change objects already written into the global state, without explicitly\n * clonning them first yourself.\n * - State update notifications are asynchronous. When your code does multiple\n * global state updates in the same React rendering cycle, all state update\n * notifications are queued and dispatched together, after the current\n * rendering cycle. In other words, in any given rendering cycle the global\n * state values are \"fixed\", and all changes becomes visible at once in the\n * next triggered rendering pass.\n *\n * @param {string} [path] Dot-delimitered state path. It can be undefined to\n * subscribe for entire state.\n *\n * Under-the-hood state values are read and written using `lodash`\n * [_.get()](https://lodash.com/docs/4.17.15#get) and\n * [_.set()](https://lodash.com/docs/4.17.15#set) methods, thus it is safe\n * to access state paths which have not been created before.\n * @param {any} [initialValue] Initial value to set at the `path`, or its\n * factory:\n * - If a function is given, it will act similar to\n * [the lazy initial state of the standard React's useState()](https://reactjs.org/docs/hooks-reference.html#lazy-initial-state):\n * only if the value at `path` is `undefined`, the function will be executed,\n * and the value it returns will be written to the `path`.\n * - Otherwise, the given value itself will be written to the `path`,\n * if the current value at `path` is `undefined`.\n * @return {Array} It returs an array with two elements: `[value, setValue]`:\n *\n * - The `value` is the current value at given `path`.\n *\n * - The `setValue()` is setter function to write a new value to the `path`.\n *\n * Similar to the standard React's `useState()`, it supports\n * [functional value updates](https://reactjs.org/docs/hooks-reference.html#functional-updates):\n * if `setValue()` is called with a function as argument, that function will\n * be called and its return value will be written to `path`. Otherwise,\n * the argument of `setValue()` itself is written to `path`.\n *\n * Also, similar to the standard React's state setters, `setValue()` is\n * stable function: it does not change between component re-renders.\n */\nexport default function useGlobalState(path, initialValue) {\n const globalState = getGlobalState();\n let state = globalState.get(path);\n if (isUndefined(state) && !isUndefined(initialValue)) {\n const value = isFunction(initialValue) ? initialValue() : initialValue;\n state = globalState.set(path, value);\n }\n const [\n localState,\n setLocalState,\n ] = useState(() => state);\n\n useEffect(() => {\n // Note: the \"callback.active\" flag below is needed to workaround the issue\n // https://github.com/birdofpreyru/react-global-state/issues/33,\n // which, unfortunately, I am not able to reproduce in test environment,\n // but I definitely seen it in the wild.\n const callback = () => {\n if (callback.active) {\n const newState = globalState.get(path);\n if (newState !== localState) setLocalState(() => newState);\n }\n };\n callback.active = true;\n globalState.watch(callback);\n callback();\n return () => {\n delete callback.active;\n globalState.unWatch(callback);\n };\n }, [globalState, localState, path]);\n\n const ref = useRef();\n if (!ref.current) {\n ref.current = {\n localState,\n path,\n setter: (value) => {\n const newValue = isFunction(value)\n ? value(ref.current.localState) : value;\n if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n /* eslint-disable no-console */\n console.groupCollapsed(\n `ReactGlobalState - useGlobalState setter triggered for path ${\n ref.current.path || ''\n }`,\n );\n console.log('New value:', cloneDeep(newValue));\n console.groupEnd();\n /* eslint-enable no-console */\n }\n\n globalState.set(ref.current.path, newValue);\n\n // The update of local state here is important for managed inputs:\n // if we wait until the global state change notification is delivered\n // (which happens after the next React render), React won't conserve\n // the text cursor inside the currently focused input field (the cursor\n // will jump to the field end, like if the value was changed not by\n // keyboard input).\n setLocalState(() => newValue);\n },\n };\n } else {\n ref.current.localState = localState;\n ref.current.path = path;\n }\n\n return [\n localState,\n ref.current.setter,\n ];\n}\n"],"file":"useGlobalState.js"}
|
package/build/web/utils.js
CHANGED
|
@@ -8,18 +8,6 @@ exports.isDebugMode = isDebugMode;
|
|
|
8
8
|
|
|
9
9
|
// Auxiliary stuff.
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* @global
|
|
13
|
-
* @const REACT_GLOBAL_STATE_DEBUG
|
|
14
|
-
* @desc `REACT_GLOBAL_STATE_DEBUG` is an environment variable you can set
|
|
15
|
-
* to any truthy value for Node (or, in case of client-side bundle, inject
|
|
16
|
-
* using Webpack's
|
|
17
|
-
* [EnvironmentPlugin](https://webpack.js.org/plugins/environment-plugin/))
|
|
18
|
-
* to enable debug logging by `react-global-state` library. In either case,
|
|
19
|
-
* the logging is enabled in non-production code only, which is tested by
|
|
20
|
-
* `process.env.NODE_ENV` value being distinct from `production`.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
11
|
/**
|
|
24
12
|
* Returns 'true' if debug logging should be performed; 'false' otherwise.
|
|
25
13
|
*
|
package/build/web/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils.js"],"names":["isDebugMode","process","env","NODE_ENV","REACT_GLOBAL_STATE_DEBUG","error"],"mappings":";;;;;;;;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA
|
|
1
|
+
{"version":3,"sources":["../../src/utils.js"],"names":["isDebugMode","process","env","NODE_ENV","REACT_GLOBAL_STATE_DEBUG","error"],"mappings":";;;;;;;;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASA,WAAT,GAAuB;AAC5B,MAAI;AACF,WAAOC,OAAO,CAACC,GAAR,CAAYC,QAAZ,KAAyB,YAAzB,IACF,CAAC,CAACF,OAAO,CAACC,GAAR,CAAYE,wBADnB;AAED,GAHD,CAGE,OAAOC,KAAP,EAAc;AACd,WAAO,KAAP;AACD;AACF;;eAEc,I","sourcesContent":["// Auxiliary stuff.\n\n/**\n * Returns 'true' if debug logging should be performed; 'false' otherwise.\n *\n * BEWARE: The actual safeguards for the debug logging still should read\n * if (process.env.NODE_ENV !== 'production' && isDebugMode()) {\n * // Some debug logging\n * }\n * to ensure that debug code is stripped out by Webpack in production mode.\n *\n * @returns {boolean}\n * @ignore\n */\nexport function isDebugMode() {\n try {\n return process.env.NODE_ENV !== 'production'\n && !!process.env.REACT_GLOBAL_STATE_DEBUG;\n } catch (error) {\n return false;\n }\n}\n\nexport default null;\n"],"file":"utils.js"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dr.pogodin/react-global-state",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"description": "Hook-based global state for React",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "rimraf build && npm run build:node && npm run build:web",
|
|
8
8
|
"build:node": "rimraf build/node && babel src --out-dir build/node --source-maps",
|
|
9
9
|
"build:web": "rimraf build/web && babel src --out-dir build/web --source-maps --config-file ./babel.web.config.js",
|
|
10
|
-
"docs": "rimraf docs && jsdoc -c config/jsdoc/config.js",
|
|
11
10
|
"jest": "jest --config jest/config.json",
|
|
12
11
|
"lint": "eslint --ext .js,.jsx .",
|
|
13
12
|
"test": "npm run lint && npm run jest"
|
|
@@ -28,33 +27,32 @@
|
|
|
28
27
|
"bugs": {
|
|
29
28
|
"url": "https://github.com/birdofpreyru/react-global-state.git/issues"
|
|
30
29
|
},
|
|
31
|
-
"homepage": "https://dr.pogodin.studio/
|
|
30
|
+
"homepage": "https://dr.pogodin.studio/docs/react-global-state/index.html",
|
|
32
31
|
"dependencies": {
|
|
33
|
-
"@babel/runtime": "^7.
|
|
32
|
+
"@babel/runtime": "^7.17.2",
|
|
34
33
|
"lodash": "^4.17.21",
|
|
35
34
|
"uuid": "^8.3.2"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|
|
38
|
-
"@babel/cli": "^7.
|
|
39
|
-
"@babel/core": "^7.
|
|
40
|
-
"@babel/
|
|
41
|
-
"@babel/plugin
|
|
42
|
-
"@babel/
|
|
43
|
-
"@babel/
|
|
44
|
-
"babel-
|
|
45
|
-
"babel-
|
|
37
|
+
"@babel/cli": "^7.17.6",
|
|
38
|
+
"@babel/core": "^7.17.5",
|
|
39
|
+
"@babel/eslint-parser": "^7.17.0",
|
|
40
|
+
"@babel/eslint-plugin": "^7.16.5",
|
|
41
|
+
"@babel/node": "^7.16.8",
|
|
42
|
+
"@babel/plugin-transform-runtime": "^7.17.0",
|
|
43
|
+
"@babel/preset-env": "^7.16.11",
|
|
44
|
+
"@babel/preset-react": "^7.16.7",
|
|
45
|
+
"babel-jest": "^27.5.1",
|
|
46
46
|
"babel-plugin-module-resolver": "^4.1.0",
|
|
47
|
-
"
|
|
48
|
-
"eslint": "^
|
|
49
|
-
"eslint-config-airbnb": "^18.2.1",
|
|
47
|
+
"eslint": "^8.10.0",
|
|
48
|
+
"eslint-config-airbnb": "^19.0.4",
|
|
50
49
|
"eslint-import-resolver-babel-module": "^5.3.1",
|
|
51
|
-
"eslint-plugin-import": "^2.25.
|
|
52
|
-
"eslint-plugin-jest": "^
|
|
53
|
-
"eslint-plugin-jsx-a11y": "^6.
|
|
54
|
-
"eslint-plugin-react": "^7.
|
|
55
|
-
"eslint-plugin-react-hooks": "^4.
|
|
56
|
-
"jest": "^27.
|
|
57
|
-
"jsdoc": "^3.6.7",
|
|
50
|
+
"eslint-plugin-import": "^2.25.4",
|
|
51
|
+
"eslint-plugin-jest": "^26.1.1",
|
|
52
|
+
"eslint-plugin-jsx-a11y": "^6.5.1",
|
|
53
|
+
"eslint-plugin-react": "^7.29.2",
|
|
54
|
+
"eslint-plugin-react-hooks": "^4.3.0",
|
|
55
|
+
"jest": "^27.5.1",
|
|
58
56
|
"mockdate": "^3.0.5",
|
|
59
57
|
"pretty": "^2.0.0",
|
|
60
58
|
"react": "^17.0.2",
|