muya 2.0.6 → 2.0.8
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/cjs/index.js +1 -1
- package/esm/create-state.js +1 -1
- package/esm/create.js +1 -1
- package/esm/select.js +1 -1
- package/esm/utils/common.js +1 -1
- package/package.json +1 -1
- package/src/__tests__/compare.test.tsx +68 -0
- package/src/__tests__/select.test.tsx +66 -3
- package/src/create-state.ts +7 -6
- package/src/create.ts +1 -0
- package/src/select.ts +49 -5
- package/src/types.ts +3 -3
- package/src/utils/common.ts +1 -1
- package/types/create-state.d.ts +1 -0
- package/types/types.d.ts +3 -3
- package/types/utils/common.d.ts +4 -0
package/cjs/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var v=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var K=(e,t)=>{for(var r in t)v(e,r,{get:t[r],enumerable:!0})},Y=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of N(t))!_.call(e,o)&&o!==r&&v(e,o,{get:()=>t[o],enumerable:!(n=j(t,o))||n.enumerable});return e};var J=e=>Y(v({},"__esModule",{value:!0}),e);var $={};K($,{EMPTY_SELECTOR:()=>F,create:()=>L,isAbortError:()=>A,isArray:()=>P,isEqualBase:()=>S,isError:()=>D,isFunction:()=>h,isMap:()=>E,isPromise:()=>f,isSet:()=>g,isSetValueFunction:()=>G,isState:()=>W,isUndefined:()=>m,select:()=>k,shallow:()=>z,useValue:()=>O});module.exports=J($);var T=class extends Error{static Error="AbortError"};function Q(e,t){t&&t.abort();let r=new AbortController,{signal:n}=r;return{promise:new Promise((u,s)=>{n.addEventListener("abort",()=>{s(new T)}),e.then(u).catch(s)}),controller:r}}function x(e,t=S){if(!m(e.current)){if(!m(e.previous)&&t(e.current,e.previous))return!1;e.previous=e.current}return!0}function p(e,t,r){if(!f(r))return r;e.abortController&&e.abortController.abort();let{promise:n,controller:o}=Q(r,e.abortController);return e.abortController=o,n.then(u=>{e.current=u,t()}).catch(u=>{A(u)||(e.current=u,t())})}function f(e){return e instanceof Promise}function h(e){return typeof e=="function"}function E(e){return e instanceof Map}function g(e){return e instanceof Set}function P(e){return Array.isArray(e)}function S(e,t){return e===t?!0:!!Object.is(e,t)}function G(e){return typeof e=="function"}function A(e){return e instanceof T}function D(e){return e instanceof Error}function m(e){return e===void 0}function W(e){return h(e)&&"get"in e&&"set"in e&&"isSet"in e&&e.isSet===!0}var F=e=>e;function q(){let e=new Map,t=new Set,r=performance.now(),n=!1;function o(){let s=performance.now(),a=s-r,{size:i}=t;if(a<.2&&i>0&&i<10){r=s,u();return}n||(n=!0,Promise.resolve().then(()=>{n=!1,r=performance.now(),u()}))}function u(){if(t.size===0)return;let s=new Set;for(let a of t){if(e.has(a.id)){s.add(a.id);let{onResolveItem:i}=e.get(a.id);i&&i(a.value)}t.delete(a)}if(t.size>0){o();return}for(let a of s)e.get(a)?.onFinish()}return{add(s,a){return e.set(s,a),()=>{e.delete(s)}},schedule(s,a){t.add({value:a,id:s}),o()}}}function k(e,t,r){let n={};function o(){let c=[],l=!1;for(let I of e){let y=I.get();f(y)&&(l=!0),c.push(y)}return l?new Promise((I,y)=>{Promise.all(c).then(U=>{if(U.some(H=>m(H)))return y(new T);let M=t(...U);I(M)})}):t(...c)}function u(){if(m(n.current)){let c=o();n.current=p(n,i.emitter.emit,c)}return n.current}function s(){if(m(n.current)){let l=o();n.current=p(n,i.emitter.emit,l)}let{current:c}=n;return f(c)?new Promise(l=>{c.then(b=>{if(m(b)){l(s());return}l(b)})}):n.current}let a=[];for(let c of e){let l=c.emitter.subscribe(()=>{w.schedule(i.id,null)});a.push(l)}let i=C({destroy(){for(let c of a)c();d(),i.emitter.clear(),n.current=void 0},get:s,getSnapshot:u}),d=w.add(i.id,{onFinish(){let c=o();n.current=p(n,i.emitter.emit,c),x(n,r)&&i.emitter.emit()}});return i}var V=require("react");function O(e,t=F){let{emitter:r}=e,n=(0,V.useSyncExternalStore)(e.emitter.subscribe,()=>t(r.getSnapshot()),()=>t(r.getInitialSnapshot?r.getInitialSnapshot():r.getSnapshot()));if((0,V.useDebugValue)(n),f(n)||D(n))throw n;return n}function R(e,t){let r=new Set,n=[];return{clear:()=>{for(let o of n)o();r.clear()},subscribe:o=>(r.add(o),()=>{r.delete(o)}),emit:(...o)=>{for(let u of r)u(...o)},contains:o=>r.has(o),getSnapshot:e,getInitialSnapshot:t,getSize:()=>r.size,subscribeToOtherEmitter(o){let u=o.subscribe(()=>{this.emit()});n.push(u)}}}var X=0;function Z(){return X++}function C(e){let{get:t,destroy:r,set:n,getSnapshot:o}=e,u=!!n,s=function(a){return O(s,a)};return s.isSet=u,s.id=Z(),s.emitter=R(o),s.destroy=r,s.listen=function(a){return this.emitter.subscribe(()=>{let i=t();f(i)||a(t())})},s.withName=function(a){return this.stateName=a,this},s.select=function(a,i=S){return k([s],a,i)},s.get=t,s.set=n,s}var w=q();function L(e,t=S){let r={};function n(){try{if(m(r.current)){let i=h(e)?e():e,d=p(r,s.emitter.emit,i);return r.current=d,r.current}return r.current}catch(i){r.current=i}return r.current}async function o(i,d){await i;let c=d(r.current),l=p(r,s.emitter.emit,c);r.current=l}function u(i){let d=n(),c=G(i);if(c&&f(d)){o(d,i);return}r.abortController&&r.abortController.abort();let l=c?i(d):i,b=p(r,s.emitter.emit,l);r.current=b}let s=C({get:n,destroy(){n(),a(),s.emitter.clear(),r.current=void 0},set(i){w.schedule(s.id,i)},getSnapshot:n}),a=w.add(s.id,{onFinish(){r.current=n(),x(r,t)&&s.emitter.emit()},onResolveItem:u});return h(e)||n(),s}function z(e,t){if(e==t)return!0;if(typeof e!="object"||e==null||typeof t!="object"||t==null)return!1;if(E(e)&&E(t)){if(e.size!==t.size)return!1;for(let[o,u]of e)if(!Object.is(u,t.get(o)))return!1;return!0}if(g(e)&&g(t)){if(e.size!==t.size)return!1;for(let o of e)if(!t.has(o))return!1;return!0}if(P(e)&&P(t)){if(e.length!==t.length)return!1;for(let[o,u]of e.entries())if(!Object.is(u,t[o]))return!1;return!0}let r=Object.keys(e),n=Object.keys(t);if(r.length!==n.length)return!1;for(let o of r)if(!Object.prototype.hasOwnProperty.call(t,o)||!Object.is(e[o],t[o]))return!1;return!0}
|
package/esm/create-state.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{select as
|
|
1
|
+
import{select as l}from"./select";import{useValue as S}from"./use-value";import{createEmitter as c}from"./utils/create-emitter";import{isEqualBase as d,isPromise as m}from"./utils/is";let T=0;function f(){return T++}function h(n){const{get:r,destroy:o,set:a,getSnapshot:i}=n,u=!!a,t=function(e){return S(t,e)};return t.isSet=u,t.id=f(),t.emitter=c(i),t.destroy=o,t.listen=function(e){return this.emitter.subscribe(()=>{const s=r();m(s)||e(r())})},t.withName=function(e){return this.stateName=e,this},t.select=function(e,s=d){return l([t],e,s)},t.get=r,t.set=a,t}export{h as createState};
|
package/esm/create.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{canUpdate as p,handleAsyncUpdate as u}from"./utils/common";import{isEqualBase as
|
|
1
|
+
import{canUpdate as p,handleAsyncUpdate as u}from"./utils/common";import{isEqualBase as h,isFunction as i,isPromise as V,isSetValueFunction as b,isUndefined as y}from"./utils/is";import{createScheduler as C}from"./scheduler";import{subscribeToDevelopmentTools as w}from"./debug/development-tools";import{createState as v}from"./create-state";const l=C();function E(c,m=h){const e={};function o(){try{if(y(e.current)){const t=i(c)?c():c,n=u(e,r.emitter.emit,t);return e.current=n,e.current}return e.current}catch(t){e.current=t}return e.current}async function d(t,n){await t;const a=n(e.current),s=u(e,r.emitter.emit,a);e.current=s}function f(t){const n=o(),a=b(t);if(a&&V(n)){d(n,t);return}e.abortController&&e.abortController.abort();const s=a?t(n):t,S=u(e,r.emitter.emit,s);e.current=S}const r=v({get:o,destroy(){o(),T(),r.emitter.clear(),e.current=void 0},set(t){l.schedule(r.id,t)},getSnapshot:o}),T=l.add(r.id,{onFinish(){e.current=o(),p(e,m)&&r.emitter.emit()},onResolveItem:f});return i(c)||o(),w(r),r}export{E as create,l as stateScheduler};
|
package/esm/select.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{stateScheduler as
|
|
1
|
+
import{stateScheduler as T}from"./create";import{createState as A}from"./create-state";import{subscribeToDevelopmentTools as P}from"./debug/development-tools";import{AbortError as g,canUpdate as V,handleAsyncUpdate as a}from"./utils/common";import{isPromise as S,isUndefined as s}from"./utils/is";function U(m,f,h){const t={};function c(){const e=[];let r=!1;for(const i of m){const o=i.get();S(o)&&(r=!0),e.push(o)}return r?new Promise((i,o)=>{Promise.all(e).then(p=>{if(p.some(b=>s(b)))return o(new g);const k=f(...p);i(k)})}):f(...e)}function w(){if(s(t.current)){const e=c();t.current=a(t,n.emitter.emit,e)}return t.current}function l(){if(s(t.current)){const r=c();t.current=a(t,n.emitter.emit,r)}const{current:e}=t;return S(e)?new Promise(r=>{e.then(u=>{if(s(u)){r(l());return}r(u)})}):t.current}const d=[];for(const e of m){const r=e.emitter.subscribe(()=>{T.schedule(n.id,null)});d.push(r)}const n=A({destroy(){for(const e of d)e();y(),n.emitter.clear(),t.current=void 0},get:l,getSnapshot:w}),y=T.add(n.id,{onFinish(){const e=c();t.current=a(t,n.emitter.emit,e),V(t,h)&&n.emitter.emit()}});return P(n),n}export{U as select};
|
package/esm/utils/common.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isAbortError as a,isEqualBase as b,isPromise as u,isUndefined as i}from"./is";class
|
|
1
|
+
import{isAbortError as a,isEqualBase as b,isPromise as u,isUndefined as i}from"./is";class p extends Error{static Error="AbortError"}function T(r,o){o&&o.abort();const t=new AbortController,{signal:n}=t;return{promise:new Promise((e,s)=>{n.addEventListener("abort",()=>{s(new p)}),r.then(e).catch(s)}),controller:t}}function f(r,o=b){if(!i(r.current)){if(!i(r.previous)&&o(r.current,r.previous))return!1;r.previous=r.current}return!0}function m(r,o,t){if(!u(t))return t;r.abortController&&r.abortController.abort();const{promise:n,controller:l}=T(t,r.abortController);return r.abortController=l,n.then(e=>{r.current=e,o()}).catch(e=>{a(e)||(r.current=e,o())})}export{p as AbortError,f as canUpdate,T as cancelablePromise,m as handleAsyncUpdate};
|
package/package.json
CHANGED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react-hooks'
|
|
2
|
+
import { create } from '../create'
|
|
3
|
+
import { longPromise } from './test-utils'
|
|
4
|
+
import { waitFor } from '@testing-library/react'
|
|
5
|
+
import { atom, useAtom, useSetAtom } from 'jotai'
|
|
6
|
+
|
|
7
|
+
describe('compare', () => {
|
|
8
|
+
it('should render async value with sync selector', async () => {
|
|
9
|
+
const state = create(longPromise(100))
|
|
10
|
+
const selectedState = state.select((value) => {
|
|
11
|
+
return value + 2
|
|
12
|
+
})
|
|
13
|
+
const listen = jest.fn()
|
|
14
|
+
|
|
15
|
+
const { result } = renderHook(() => {
|
|
16
|
+
listen()
|
|
17
|
+
const value = selectedState()
|
|
18
|
+
return value
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
await waitFor(() => {
|
|
22
|
+
expect(result.current).toBe(2)
|
|
23
|
+
expect(listen).toHaveBeenCalledTimes(2)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
state.set(1)
|
|
27
|
+
|
|
28
|
+
await waitFor(() => {
|
|
29
|
+
expect(result.current).toBe(3)
|
|
30
|
+
// muya re-render only 3 times
|
|
31
|
+
expect(listen).toHaveBeenCalledTimes(3)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
it('should render async value with sync selector with jotai', async () => {
|
|
35
|
+
const state = atom(longPromise(100))
|
|
36
|
+
|
|
37
|
+
const selectedState = atom(async (get) => {
|
|
38
|
+
const value = get(state)
|
|
39
|
+
return (await value) + 2
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const listen = jest.fn()
|
|
43
|
+
const { result: setResult } = renderHook(() => {
|
|
44
|
+
const value = useSetAtom(state)
|
|
45
|
+
return value
|
|
46
|
+
})
|
|
47
|
+
const { result } = renderHook(() => {
|
|
48
|
+
listen()
|
|
49
|
+
const value = useAtom(selectedState)
|
|
50
|
+
return value
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
await waitFor(() => {
|
|
54
|
+
expect(result.current[0]).toBe(2)
|
|
55
|
+
expect(listen).toHaveBeenCalledTimes(3)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
act(() => {
|
|
59
|
+
setResult.current(1 as never)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
await waitFor(() => {
|
|
63
|
+
expect(result.current[0]).toBe(3)
|
|
64
|
+
// OH it render 5 times, sad.
|
|
65
|
+
expect(listen).toHaveBeenCalledTimes(5)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { create } from '../create'
|
|
2
2
|
import { select } from '../select'
|
|
3
|
-
import { waitFor } from '@testing-library/react'
|
|
3
|
+
import { renderHook, waitFor } from '@testing-library/react'
|
|
4
4
|
import { longPromise } from './test-utils'
|
|
5
|
+
import { Suspense } from 'react'
|
|
5
6
|
|
|
6
7
|
describe('select', () => {
|
|
7
8
|
it('should derive state from a single dependency', async () => {
|
|
@@ -101,7 +102,7 @@ describe('select', () => {
|
|
|
101
102
|
await longPromise(100)
|
|
102
103
|
return (await value) + 1
|
|
103
104
|
})
|
|
104
|
-
const selectedState2 = selectedState.select(async (value) =>
|
|
105
|
+
const selectedState2 = selectedState.select(async (value) => value + 1)
|
|
105
106
|
const listener = jest.fn()
|
|
106
107
|
selectedState2.listen(listener)
|
|
107
108
|
await waitFor(() => {
|
|
@@ -154,7 +155,7 @@ describe('select', () => {
|
|
|
154
155
|
it('should select state from async initial state', async () => {
|
|
155
156
|
const state = create(longPromise(100))
|
|
156
157
|
const selectedState = state.select(async (value) => {
|
|
157
|
-
return
|
|
158
|
+
return value + 2
|
|
158
159
|
})
|
|
159
160
|
await waitFor(() => {
|
|
160
161
|
expect(selectedState.get()).toBe(2)
|
|
@@ -169,4 +170,66 @@ describe('select', () => {
|
|
|
169
170
|
expect(selectedState.get()).toBe(2)
|
|
170
171
|
})
|
|
171
172
|
})
|
|
173
|
+
|
|
174
|
+
it('should select state from async state and do not change second time as it just boolean value', async () => {
|
|
175
|
+
const state = create(longPromise(100))
|
|
176
|
+
const selectedState = state.select((value) => {
|
|
177
|
+
const result = value > 0
|
|
178
|
+
expect(value).not.toBeUndefined()
|
|
179
|
+
return result
|
|
180
|
+
})
|
|
181
|
+
const render = jest.fn()
|
|
182
|
+
|
|
183
|
+
const { result } = renderHook(
|
|
184
|
+
() => {
|
|
185
|
+
render()
|
|
186
|
+
const value = selectedState()
|
|
187
|
+
return value
|
|
188
|
+
},
|
|
189
|
+
{ wrapper: ({ children }) => <Suspense fallback="loading">{children}</Suspense> },
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
await waitFor(() => {
|
|
193
|
+
expect(result.current).toBe(false)
|
|
194
|
+
expect(selectedState.get()).toBe(false)
|
|
195
|
+
// re-render twice, as it hit suspense, because value is not resolved yet
|
|
196
|
+
expect(render).toHaveBeenCalledTimes(2)
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
state.set(1)
|
|
200
|
+
|
|
201
|
+
await waitFor(() => {
|
|
202
|
+
expect(result.current).toBe(true)
|
|
203
|
+
expect(selectedState.get()).toBe(true)
|
|
204
|
+
// next time it re-render only once, as value is already resolved
|
|
205
|
+
expect(render).toHaveBeenCalledTimes(3)
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it('should get value when initial value is promise', async () => {
|
|
210
|
+
const state = create(longPromise(100))
|
|
211
|
+
const selectedState = state.select((value) => {
|
|
212
|
+
return value + 2
|
|
213
|
+
})
|
|
214
|
+
expect(await selectedState.get()).toBe(2)
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
it('should have sync get method sync', async () => {
|
|
218
|
+
const state = create(100)
|
|
219
|
+
const selectedState = state.select((value) => {
|
|
220
|
+
return value + 2
|
|
221
|
+
})
|
|
222
|
+
expect(selectedState.get()).toBe(102)
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('should have async get method sync', async () => {
|
|
226
|
+
const state = create(100)
|
|
227
|
+
const selectedState = state.select((value) => {
|
|
228
|
+
return value + 2
|
|
229
|
+
})
|
|
230
|
+
const selectedState2 = selectedState.select(async (value) => {
|
|
231
|
+
return value + 2
|
|
232
|
+
})
|
|
233
|
+
expect(await selectedState2.get()).toBe(104)
|
|
234
|
+
})
|
|
172
235
|
})
|
package/src/create-state.ts
CHANGED
|
@@ -8,6 +8,7 @@ interface GetStateOptions<T> {
|
|
|
8
8
|
readonly get: () => T
|
|
9
9
|
readonly set?: (value: SetValue<T>) => void
|
|
10
10
|
readonly destroy: () => void
|
|
11
|
+
readonly getSnapshot: () => T
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
let stateId = 0
|
|
@@ -20,7 +21,7 @@ type FullState<T> = GetStateOptions<T>['set'] extends undefined ? GetState<T> :
|
|
|
20
21
|
* This is just utility function to create state base data
|
|
21
22
|
*/
|
|
22
23
|
export function createState<T>(options: GetStateOptions<T>): FullState<T> {
|
|
23
|
-
const { get, destroy, set } = options
|
|
24
|
+
const { get, destroy, set, getSnapshot } = options
|
|
24
25
|
const isSet = !!set
|
|
25
26
|
|
|
26
27
|
const state: FullState<T> = function (selector) {
|
|
@@ -29,7 +30,7 @@ export function createState<T>(options: GetStateOptions<T>): FullState<T> {
|
|
|
29
30
|
}
|
|
30
31
|
state.isSet = isSet as true
|
|
31
32
|
state.id = getStateId()
|
|
32
|
-
state.emitter = createEmitter<T>(
|
|
33
|
+
state.emitter = createEmitter<T>(getSnapshot)
|
|
33
34
|
state.destroy = destroy
|
|
34
35
|
state.listen = function (listener) {
|
|
35
36
|
return this.emitter.subscribe(() => {
|
|
@@ -44,10 +45,10 @@ export function createState<T>(options: GetStateOptions<T>): FullState<T> {
|
|
|
44
45
|
this.stateName = name
|
|
45
46
|
return this
|
|
46
47
|
}
|
|
47
|
-
state.select = function (selector, isSelectorEqual = isEqualBase) {
|
|
48
|
-
return select([state], selector, isSelectorEqual)
|
|
49
|
-
}
|
|
50
|
-
state.get = get
|
|
48
|
+
state.select = function (selector: never, isSelectorEqual = isEqualBase) {
|
|
49
|
+
return select([state as never], selector, isSelectorEqual)
|
|
50
|
+
} as never
|
|
51
|
+
state.get = get as never
|
|
51
52
|
state.set = set as State<T>['set']
|
|
52
53
|
|
|
53
54
|
return state
|
package/src/create.ts
CHANGED
package/src/select.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { stateScheduler } from './create'
|
|
|
2
2
|
import { createState } from './create-state'
|
|
3
3
|
import { subscribeToDevelopmentTools } from './debug/development-tools'
|
|
4
4
|
import type { Cache, GetState, IsEqual } from './types'
|
|
5
|
-
import { canUpdate, handleAsyncUpdate } from './utils/common'
|
|
6
|
-
import { isUndefined } from './utils/is'
|
|
5
|
+
import { AbortError, canUpdate, handleAsyncUpdate } from './utils/common'
|
|
6
|
+
import { isPromise, isUndefined } from './utils/is'
|
|
7
7
|
|
|
8
8
|
type StateDependencies<T extends Array<unknown>> = {
|
|
9
9
|
[K in keyof T]: GetState<T[K]>
|
|
@@ -21,15 +21,58 @@ export function select<T = unknown, S extends Array<unknown> = []>(
|
|
|
21
21
|
const cache: Cache<T> = {}
|
|
22
22
|
|
|
23
23
|
function computedValue(): T {
|
|
24
|
-
const values = states.map((state) => state.get()) as S
|
|
25
|
-
return selector(...values)
|
|
26
|
-
}
|
|
24
|
+
// const values = states.map((state) => state.get()) as S
|
|
27
25
|
|
|
26
|
+
const values: unknown[] = []
|
|
27
|
+
let hasPromise = false
|
|
28
|
+
for (const state of states) {
|
|
29
|
+
const value = state.get()
|
|
30
|
+
if (isPromise(value)) {
|
|
31
|
+
hasPromise = true
|
|
32
|
+
}
|
|
33
|
+
values.push(value)
|
|
34
|
+
}
|
|
35
|
+
if (hasPromise) {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
Promise.all(values).then((resolvedValues) => {
|
|
38
|
+
// check if some of value is undefined
|
|
39
|
+
// eslint-disable-next-line sonarjs/no-nested-functions
|
|
40
|
+
if (resolvedValues.some((element) => isUndefined(element))) {
|
|
41
|
+
return reject(new AbortError())
|
|
42
|
+
}
|
|
43
|
+
const resolved = selector(...(resolvedValues as S))
|
|
44
|
+
resolve(resolved)
|
|
45
|
+
})
|
|
46
|
+
}) as T
|
|
47
|
+
}
|
|
48
|
+
const result = selector(...(values as S))
|
|
49
|
+
return result
|
|
50
|
+
}
|
|
51
|
+
function getSnapshot(): T {
|
|
52
|
+
if (isUndefined(cache.current)) {
|
|
53
|
+
const newValue = computedValue()
|
|
54
|
+
cache.current = handleAsyncUpdate(cache, state.emitter.emit, newValue)
|
|
55
|
+
}
|
|
56
|
+
return cache.current
|
|
57
|
+
}
|
|
28
58
|
function getValue(): T {
|
|
29
59
|
if (isUndefined(cache.current)) {
|
|
30
60
|
const newValue = computedValue()
|
|
31
61
|
cache.current = handleAsyncUpdate(cache, state.emitter.emit, newValue)
|
|
32
62
|
}
|
|
63
|
+
const { current } = cache
|
|
64
|
+
if (isPromise(current)) {
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
current.then((value: unknown) => {
|
|
67
|
+
if (isUndefined(value)) {
|
|
68
|
+
resolve(getValue())
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
resolve(value)
|
|
73
|
+
})
|
|
74
|
+
}) as T
|
|
75
|
+
}
|
|
33
76
|
return cache.current
|
|
34
77
|
}
|
|
35
78
|
|
|
@@ -51,6 +94,7 @@ export function select<T = unknown, S extends Array<unknown> = []>(
|
|
|
51
94
|
cache.current = undefined
|
|
52
95
|
},
|
|
53
96
|
get: getValue,
|
|
97
|
+
getSnapshot,
|
|
54
98
|
})
|
|
55
99
|
|
|
56
100
|
const clearScheduler = stateScheduler.add(state.id, {
|
package/src/types.ts
CHANGED
|
@@ -13,12 +13,12 @@ export interface Cache<T> {
|
|
|
13
13
|
|
|
14
14
|
export const EMPTY_SELECTOR = <T, S>(stateValue: T) => stateValue as unknown as S
|
|
15
15
|
|
|
16
|
-
export interface GetState<T> {
|
|
16
|
+
export interface GetState<T, IsFroMPromise extends boolean = false> {
|
|
17
17
|
<S>(selector?: (stateValue: T) => S): Awaited<undefined extends S ? T : S>
|
|
18
18
|
/**
|
|
19
19
|
* Get the cached state value.
|
|
20
20
|
*/
|
|
21
|
-
get: () => T
|
|
21
|
+
get: () => IsFroMPromise extends true ? Promise<T> : T
|
|
22
22
|
/**
|
|
23
23
|
* Get the unique id of the state.
|
|
24
24
|
*/
|
|
@@ -48,7 +48,7 @@ export interface GetState<T> {
|
|
|
48
48
|
* Select particular slice of the state.
|
|
49
49
|
* It will create "another" state in read-only mode (without set).
|
|
50
50
|
*/
|
|
51
|
-
select: <S>(selector: (state: Awaited<T>
|
|
51
|
+
select: <S>(selector: (state: Awaited<T>) => S, isEqual?: IsEqual<S>) => GetState<S, T extends Promise<unknown> ? true : false>
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
export interface State<T> extends GetState<T> {
|
package/src/utils/common.ts
CHANGED
|
@@ -12,7 +12,7 @@ export class AbortError extends Error {
|
|
|
12
12
|
/**
|
|
13
13
|
* Cancelable promise function, return promise and controller
|
|
14
14
|
*/
|
|
15
|
-
function cancelablePromise<T>(promise: Promise<T>, previousController?: AbortController): CancelablePromise<T> {
|
|
15
|
+
export function cancelablePromise<T>(promise: Promise<T>, previousController?: AbortController): CancelablePromise<T> {
|
|
16
16
|
if (previousController) {
|
|
17
17
|
previousController.abort()
|
|
18
18
|
}
|
package/types/create-state.d.ts
CHANGED
package/types/types.d.ts
CHANGED
|
@@ -10,12 +10,12 @@ export interface Cache<T> {
|
|
|
10
10
|
abortController?: AbortController;
|
|
11
11
|
}
|
|
12
12
|
export declare const EMPTY_SELECTOR: <T, S>(stateValue: T) => S;
|
|
13
|
-
export interface GetState<T> {
|
|
13
|
+
export interface GetState<T, IsFroMPromise extends boolean = false> {
|
|
14
14
|
<S>(selector?: (stateValue: T) => S): Awaited<undefined extends S ? T : S>;
|
|
15
15
|
/**
|
|
16
16
|
* Get the cached state value.
|
|
17
17
|
*/
|
|
18
|
-
get: () => T;
|
|
18
|
+
get: () => IsFroMPromise extends true ? Promise<T> : T;
|
|
19
19
|
/**
|
|
20
20
|
* Get the unique id of the state.
|
|
21
21
|
*/
|
|
@@ -45,7 +45,7 @@ export interface GetState<T> {
|
|
|
45
45
|
* Select particular slice of the state.
|
|
46
46
|
* It will create "another" state in read-only mode (without set).
|
|
47
47
|
*/
|
|
48
|
-
select: <S>(selector: (state: Awaited<T>
|
|
48
|
+
select: <S>(selector: (state: Awaited<T>) => S, isEqual?: IsEqual<S>) => GetState<S, T extends Promise<unknown> ? true : false>;
|
|
49
49
|
}
|
|
50
50
|
export interface State<T> extends GetState<T> {
|
|
51
51
|
/**
|
package/types/utils/common.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ export interface CancelablePromise<T> {
|
|
|
6
6
|
export declare class AbortError extends Error {
|
|
7
7
|
static readonly Error = "AbortError";
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Cancelable promise function, return promise and controller
|
|
11
|
+
*/
|
|
12
|
+
export declare function cancelablePromise<T>(promise: Promise<T>, previousController?: AbortController): CancelablePromise<T>;
|
|
9
13
|
/**
|
|
10
14
|
* Check if the cache value is different from the previous value.
|
|
11
15
|
*/
|