muya 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -14
- package/cjs/index.js +1 -1
- package/esm/__tests__/common.test.js +1 -0
- package/esm/__tests__/is.test.js +1 -0
- package/esm/__tests__/merge.test.js +1 -0
- package/esm/__tests__/shallow.test.js +1 -0
- package/esm/__tests__/test-utils.js +1 -0
- package/esm/__tests__/types.test.js +1 -0
- package/esm/common.js +1 -1
- package/esm/create-base-state.js +1 -1
- package/esm/create-getter-state.js +1 -1
- package/esm/create.js +1 -1
- package/esm/is.js +1 -1
- package/esm/merge.js +1 -1
- package/esm/shallow.js +1 -1
- package/esm/types.js +1 -1
- package/esm/use-state-value.js +1 -1
- package/package.json +4 -1
- package/src/__tests__/common.test.ts +63 -0
- package/src/__tests__/create.test.tsx +84 -0
- package/src/__tests__/is.test.ts +103 -0
- package/src/__tests__/merge.test.ts +78 -0
- package/src/__tests__/shallow.test.ts +418 -0
- package/src/{state.test.tsx → __tests__/state.test.tsx} +2 -30
- package/src/__tests__/test-utils.ts +40 -0
- package/src/__tests__/types.test.ts +17 -0
- package/src/common.ts +32 -0
- package/src/create-base-state.ts +1 -5
- package/src/create-getter-state.ts +0 -1
- package/src/create.ts +33 -8
- package/src/is.ts +9 -0
- package/src/merge.ts +11 -14
- package/src/shallow.ts +0 -3
- package/src/types.ts +1 -1
- package/src/use-state-value.ts +4 -1
- package/types/__tests__/test-utils.d.ts +20 -0
- package/types/common.d.ts +10 -0
- package/types/is.d.ts +2 -0
- package/types/merge.d.ts +3 -1
- package/types/types.d.ts +1 -1
package/src/create.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createEmitter } from './create-emitter'
|
|
2
2
|
import type { SetValue, SetterState, StateDataInternal, DefaultValue, GetterState, IsEqual, UpdateValue } from './types'
|
|
3
3
|
import { getDefaultValue } from './types'
|
|
4
|
-
import { isEqualBase, isObject, isPromise, isSetValueFunction } from './is'
|
|
4
|
+
import { isAbortError, isEqualBase, isObject, isPromise, isSetValueFunction } from './is'
|
|
5
5
|
import { createBaseState } from './create-base-state'
|
|
6
6
|
import { createGetterState } from './create-getter-state'
|
|
7
|
+
import { cancelablePromise } from './common'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Creates a basic atom state.
|
|
@@ -44,19 +45,34 @@ export function create<T>(defaultValue: DefaultValue<T>, isEqual: IsEqual<T> = i
|
|
|
44
45
|
}
|
|
45
46
|
return stateData.value
|
|
46
47
|
}
|
|
48
|
+
|
|
47
49
|
function get(): T {
|
|
48
50
|
const stateValue = getValue()
|
|
49
51
|
if (isPromise(stateValue)) {
|
|
50
|
-
stateValue.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
const { controller, promise } = cancelablePromise(stateValue, stateData.abortController)
|
|
53
|
+
stateData.abortController = controller
|
|
54
|
+
promise
|
|
55
|
+
.then((data) => {
|
|
56
|
+
stateData.value = data as Awaited<T>
|
|
57
|
+
emitter.emit()
|
|
58
|
+
})
|
|
59
|
+
.catch((error) => {
|
|
60
|
+
if (isAbortError(error)) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
stateData.value = new Error(error) as T
|
|
64
|
+
})
|
|
54
65
|
}
|
|
55
66
|
return stateValue
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
function set(stateValue: SetValue<T>) {
|
|
59
70
|
const stateValueData = getValue()
|
|
71
|
+
if (stateData.abortController) {
|
|
72
|
+
stateData.abortController.abort()
|
|
73
|
+
stateData.abortController = undefined
|
|
74
|
+
}
|
|
75
|
+
|
|
60
76
|
const newState = resolveSetter(stateValueData, stateValue)
|
|
61
77
|
const isEqualResult = isEqual?.(stateValueData, newState)
|
|
62
78
|
if (isEqualResult || newState === stateValueData) {
|
|
@@ -85,9 +101,18 @@ export function create<T>(defaultValue: DefaultValue<T>, isEqual: IsEqual<T> = i
|
|
|
85
101
|
reset() {
|
|
86
102
|
const value = getDefaultValue(defaultValue)
|
|
87
103
|
if (isPromise(value)) {
|
|
88
|
-
value.
|
|
89
|
-
|
|
90
|
-
|
|
104
|
+
const { controller, promise } = cancelablePromise(value, stateData.abortController)
|
|
105
|
+
stateData.abortController = controller
|
|
106
|
+
promise
|
|
107
|
+
.then((data) => {
|
|
108
|
+
set(data as T)
|
|
109
|
+
})
|
|
110
|
+
.catch((error) => {
|
|
111
|
+
if (isAbortError(error)) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
stateData.value = new Error(error) as T
|
|
115
|
+
})
|
|
91
116
|
return
|
|
92
117
|
}
|
|
93
118
|
set(value)
|
package/src/is.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Abort } from './common'
|
|
1
2
|
import type { Ref, Setter, SetValue } from './types'
|
|
2
3
|
|
|
3
4
|
export function isPromise(value: unknown): value is Promise<unknown> {
|
|
@@ -34,3 +35,11 @@ export function isEqualBase<T>(valueA: T, valueB: T): boolean {
|
|
|
34
35
|
}
|
|
35
36
|
return !!Object.is(valueA, valueB)
|
|
36
37
|
}
|
|
38
|
+
|
|
39
|
+
export function isAbortError(value: unknown): value is DOMException {
|
|
40
|
+
return value instanceof DOMException && value.name === Abort.Error
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function isAnyOtherError(value: unknown): value is Error {
|
|
44
|
+
return value instanceof Error && value.name !== Abort.Error
|
|
45
|
+
}
|
package/src/merge.ts
CHANGED
|
@@ -4,35 +4,32 @@ import { createGetterState } from './create-getter-state'
|
|
|
4
4
|
import { isEqualBase } from './is'
|
|
5
5
|
import type { IsEqual, GetterState } from './types'
|
|
6
6
|
|
|
7
|
-
export function merge<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
selector: (value1: T1, value2: T2) => S,
|
|
7
|
+
export function merge<T extends unknown[], S>(
|
|
8
|
+
states: { [K in keyof T]: GetterState<T[K]> },
|
|
9
|
+
selector: (...values: T) => S,
|
|
11
10
|
isEqual: IsEqual<S> = isEqualBase,
|
|
12
11
|
): GetterState<S> {
|
|
13
12
|
let previousData: S | undefined
|
|
14
13
|
const emitter = createEmitter(() => {
|
|
15
|
-
const data = selector(
|
|
14
|
+
const data = selector(...(states.map((state) => state.getState()) as T))
|
|
16
15
|
if (previousData !== undefined && isEqual(previousData, data)) {
|
|
17
16
|
return previousData
|
|
18
17
|
}
|
|
19
18
|
previousData = data
|
|
20
19
|
return data
|
|
21
20
|
})
|
|
22
|
-
|
|
23
|
-
emitter.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
})
|
|
21
|
+
for (const state of states) {
|
|
22
|
+
state.__internal.emitter.subscribe(() => {
|
|
23
|
+
emitter.emit()
|
|
24
|
+
})
|
|
25
|
+
}
|
|
28
26
|
|
|
29
27
|
const baseState = createBaseState<S>({
|
|
30
28
|
emitter,
|
|
31
29
|
getGetterState: () => getterState,
|
|
32
|
-
getState: () => selector(
|
|
30
|
+
getState: () => selector(...(states.map((state) => state.getState()) as T)),
|
|
33
31
|
reset() {
|
|
34
|
-
|
|
35
|
-
state2.reset()
|
|
32
|
+
for (const state of states) state.reset()
|
|
36
33
|
},
|
|
37
34
|
})
|
|
38
35
|
|
package/src/shallow.ts
CHANGED
|
@@ -5,9 +5,6 @@ export function shallow<T>(valueA: T, valueB: T): boolean {
|
|
|
5
5
|
if (valueA == valueB) {
|
|
6
6
|
return true
|
|
7
7
|
}
|
|
8
|
-
if (Object.is(valueA, valueB)) {
|
|
9
|
-
return true
|
|
10
|
-
}
|
|
11
8
|
|
|
12
9
|
if (typeof valueA !== 'object' || valueA == null || typeof valueB !== 'object' || valueB == null) {
|
|
13
10
|
return false
|
package/src/types.ts
CHANGED
|
@@ -27,6 +27,7 @@ export type GetState<T> = () => T
|
|
|
27
27
|
export interface StateDataInternal<T = unknown> {
|
|
28
28
|
value?: T
|
|
29
29
|
updateVersion: number
|
|
30
|
+
abortController?: AbortController
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
// eslint-disable-next-line no-shadow
|
|
@@ -46,7 +47,6 @@ export interface BaseState<T> {
|
|
|
46
47
|
getState: GetState<T>
|
|
47
48
|
|
|
48
49
|
select: <S>(selector: (value: T) => S, isEqual?: IsEqual<S>) => GetterState<S>
|
|
49
|
-
merge: <T2, S>(state2: GetterState<T2>, selector: (value1: T, value2: T2) => S, isEqual?: IsEqual<S>) => GetterState<S>
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Internal state data
|
package/src/use-state-value.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IsEqual, State } from './types'
|
|
2
2
|
import { useSyncExternalStore, toType } from './common'
|
|
3
|
-
import { isPromise } from './is'
|
|
3
|
+
import { isAnyOtherError, isPromise } from './is'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* useCachedStateValue Hook.
|
|
@@ -25,5 +25,8 @@ export function useStateValue<T, S>(
|
|
|
25
25
|
if (isPromise(data)) {
|
|
26
26
|
throw data
|
|
27
27
|
}
|
|
28
|
+
if (isAnyOtherError(data)) {
|
|
29
|
+
throw data
|
|
30
|
+
}
|
|
28
31
|
return data
|
|
29
32
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Component } from 'react';
|
|
2
|
+
export declare function longPromise(time?: number): Promise<number>;
|
|
3
|
+
export declare class ErrorBoundary extends Component<{
|
|
4
|
+
fallback: React.ReactNode;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
}, {
|
|
7
|
+
hasError: boolean;
|
|
8
|
+
error: Error | null;
|
|
9
|
+
}> {
|
|
10
|
+
constructor(props: {
|
|
11
|
+
fallback: React.ReactNode;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
});
|
|
14
|
+
static getDerivedStateFromError(error: Error): {
|
|
15
|
+
hasError: boolean;
|
|
16
|
+
error: Error;
|
|
17
|
+
};
|
|
18
|
+
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
|
|
19
|
+
render(): import("react").ReactNode;
|
|
20
|
+
}
|
package/types/common.d.ts
CHANGED
|
@@ -5,3 +5,13 @@ import type { IsEqual } from './types';
|
|
|
5
5
|
*/
|
|
6
6
|
export declare function toType<T>(object?: unknown): T;
|
|
7
7
|
export declare function useSyncExternalStore<T, S>(emitter: Emitter<T>, selector: (stateValue: T) => S, isEqual?: IsEqual<S>): undefined extends S ? T : S;
|
|
8
|
+
export declare enum Abort {
|
|
9
|
+
Error = "StateAbortError"
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Cancelable promise function, return promise and controller
|
|
13
|
+
*/
|
|
14
|
+
export declare function cancelablePromise<T>(promise: Promise<T>, previousController?: AbortController): {
|
|
15
|
+
promise: Promise<T>;
|
|
16
|
+
controller: AbortController;
|
|
17
|
+
};
|
package/types/is.d.ts
CHANGED
|
@@ -8,3 +8,5 @@ export declare function isMap(value: unknown): value is Map<unknown, unknown>;
|
|
|
8
8
|
export declare function isSet(value: unknown): value is Set<unknown>;
|
|
9
9
|
export declare function isArray(value: unknown): value is Array<unknown>;
|
|
10
10
|
export declare function isEqualBase<T>(valueA: T, valueB: T): boolean;
|
|
11
|
+
export declare function isAbortError(value: unknown): value is DOMException;
|
|
12
|
+
export declare function isAnyOtherError(value: unknown): value is Error;
|
package/types/merge.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import type { IsEqual, GetterState } from './types';
|
|
2
|
-
export declare function merge<
|
|
2
|
+
export declare function merge<T extends unknown[], S>(states: {
|
|
3
|
+
[K in keyof T]: GetterState<T[K]>;
|
|
4
|
+
}, selector: (...values: T) => S, isEqual?: IsEqual<S>): GetterState<S>;
|
package/types/types.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type GetState<T> = () => T;
|
|
|
22
22
|
export interface StateDataInternal<T = unknown> {
|
|
23
23
|
value?: T;
|
|
24
24
|
updateVersion: number;
|
|
25
|
+
abortController?: AbortController;
|
|
25
26
|
}
|
|
26
27
|
export declare enum StateKeys {
|
|
27
28
|
IS_STATE = "isState",
|
|
@@ -37,7 +38,6 @@ export interface BaseState<T> {
|
|
|
37
38
|
*/
|
|
38
39
|
getState: GetState<T>;
|
|
39
40
|
select: <S>(selector: (value: T) => S, isEqual?: IsEqual<S>) => GetterState<S>;
|
|
40
|
-
merge: <T2, S>(state2: GetterState<T2>, selector: (value1: T, value2: T2) => S, isEqual?: IsEqual<S>) => GetterState<S>;
|
|
41
41
|
/**
|
|
42
42
|
* Internal state data
|
|
43
43
|
*/
|