bansa 0.0.2 → 0.0.4
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.ko.md +7 -7
- package/README.md +3 -3
- package/dist/index.browser.js +1 -1
- package/dist/index.js +8 -3
- package/dist/react.d.ts +0 -1
- package/dist/react.js +0 -1
- package/package.json +9 -11
- package/src/index.ts +0 -604
- package/src/react.tsx +0 -28
package/README.ko.md
CHANGED
|
@@ -30,7 +30,7 @@ const $user = $({ name: 'John Doe', age: 30 });
|
|
|
30
30
|
|
|
31
31
|
#### 파생 상태
|
|
32
32
|
|
|
33
|
-
값이 동적이며, 생명 주기를 가지는 상태입니다. 값을 직접 업데이트할 수 없으며, 의존 중인 상태의 값이 바뀔 때에만 재실행될 수 있습니다. 해당 상태를 구독 중인 곳이
|
|
33
|
+
값이 동적이며, 생명 주기를 가지는 상태입니다. 값을 직접 업데이트할 수 없으며, 의존 중인 상태의 값이 바뀔 때에만 재실행될 수 있습니다. 활성화된 상태가 아니라면, 즉 해당 상태를 구독 중인 곳이 존재하지 않는다면 함수는 실행되지 않으며, 의존성 또한 없는 것으로 취급됩니다.
|
|
34
34
|
|
|
35
35
|
`$`에 함수를 전달하여 생성합니다. 전달하는 함수의 인자로는 다른 상태의 값을 읽을 수 있는 `get` 함수와 상태의 수명을 나타내는 `{ signal }`이 주어집니다. `signal`에 대해선 다른 파트에서 더 자세히 다룹니다.
|
|
36
36
|
|
|
@@ -82,7 +82,7 @@ type AtomState<Value> =
|
|
|
82
82
|
console.log($count.get()); // 42
|
|
83
83
|
console.log($countDouble.get()); // 84
|
|
84
84
|
|
|
85
|
-
console.log($countDouble.state); // { promise: undefined, error:
|
|
85
|
+
console.log($countDouble.state); // { promise: undefined, error: undefined, value: 84 }
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
파생 상태는 `.get()` 했을 때 throw 될 수 있습니다. 비동기 로딩 중일 땐 해당 `Promise`를 throw하며, 오류 상태일 때는 해당 오류를 throw합니다. 값이 성공적으로 계산된 상황 위주로 처리하고, 예외 상황은 전부 `catch` 블록 등으로 밀어넣고 싶은 상황에서 유용합니다.
|
|
@@ -213,7 +213,7 @@ const $user = $(
|
|
|
213
213
|
);
|
|
214
214
|
|
|
215
215
|
const $user2 = $(
|
|
216
|
-
(get) => get($user)
|
|
216
|
+
(get) => get($user),
|
|
217
217
|
{ equals: (next, prev) => next.name === prev.name },
|
|
218
218
|
);
|
|
219
219
|
|
|
@@ -232,10 +232,10 @@ userAtom.set({ id: 2, name: 'Alice' });
|
|
|
232
232
|
|
|
233
233
|
```javascript
|
|
234
234
|
const timer = (time) => new Promise((resolve) => setTimeout(() => resolve(1), time));
|
|
235
|
-
const
|
|
236
|
-
const
|
|
235
|
+
const timerAtoms = [1, 2, 3, 4, 5].map(() => $(() => timer(1000)));
|
|
236
|
+
const $timers = $$((get) => timerAtoms.map(get));
|
|
237
237
|
console.time();
|
|
238
|
-
|
|
238
|
+
$timers.subscribe(() => console.timeEnd());
|
|
239
239
|
```
|
|
240
240
|
|
|
241
241
|
참고로 `$$`의 `get` 함수가 `Promise`나 에러를 만났을 때 throw 대신 반환하는 값은 다음 과정으로 만들어집니다:
|
|
@@ -246,7 +246,7 @@ const toUndefined = () => undefined;
|
|
|
246
246
|
Object.setPrototypeOf(o, new Proxy(o, { get: (_, k) => k === Symbol.toPrimitive ? toUndefined : o }));
|
|
247
247
|
```
|
|
248
248
|
|
|
249
|
-
이 코드의 `o`는 아무리 프로퍼티 접근 및 호출을 해도 같은 값을 반환합니다. `o.a.b.c().d()().asdf()()()() === o`는 `true`입니다. 따라서, 셀렉터와 filter/map/reduce 등 간단한 메서드로 이뤄진 대부분의 상태 병합 함수에서 문제 없이 전체 코드를 실행할 수 있게 만듭니다. 하지만 만능은 아니므로 약간의 주의가 필요하며, 가급적 상태 병합에만 사용해야 합니다.
|
|
249
|
+
이 코드의 `o`는 아무리 프로퍼티 접근 및 호출을 해도 같은 값을 반환합니다. 예를 들어, `o.a.b.c().d()().asdf()()()() === o`는 `true`입니다. 따라서, 셀렉터와 filter/map/reduce 등 간단한 메서드로 이뤄진 대부분의 상태 병합 함수에서 문제 없이 전체 코드를 실행할 수 있게 만듭니다. 하지만 만능은 아니므로 약간의 주의가 필요하며, 가급적 상태 병합에만 사용해야 합니다.
|
|
250
250
|
|
|
251
251
|
## 상세
|
|
252
252
|
|
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ You can read the current value of a state using the `atom.get()` method or `atom
|
|
|
82
82
|
console.log($count.get()); // 42
|
|
83
83
|
console.log($countDouble.get()); // 84
|
|
84
84
|
|
|
85
|
-
console.log($countDouble.state); // { promise: undefined, error:
|
|
85
|
+
console.log($countDouble.state); // { promise: undefined, error: undefined, value: 84 }
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
For derived states, `.get()` can throw. It throws the `Promise` during asynchronous loading and throws the error when in an error state. This is useful when you want to primarily handle the success case and push all exception handling into a `catch` block or similar.
|
|
@@ -213,7 +213,7 @@ const $user = $(
|
|
|
213
213
|
);
|
|
214
214
|
|
|
215
215
|
const $user2 = $(
|
|
216
|
-
(get) => get($user)
|
|
216
|
+
(get) => get($user),
|
|
217
217
|
{ equals: (next, prev) => next.name === prev.name },
|
|
218
218
|
);
|
|
219
219
|
|
|
@@ -246,7 +246,7 @@ const toUndefined = () => undefined;
|
|
|
246
246
|
Object.setPrototypeOf(o, new Proxy(o, { get: (_, k) => k === Symbol.toPrimitive ? toUndefined : o }));
|
|
247
247
|
```
|
|
248
248
|
|
|
249
|
-
The `o` in this code returns the same value no matter how many properties are accessed or functions are called. `o.a.b.c().d()().asdf()()()() === o` is `true`. Therefore, it allows most state-merging functions composed of selectors and simple methods like filter/map/reduce to execute without issues. However, it's not a silver bullet, so some caution is needed, and it should preferably be used only for state merging.
|
|
249
|
+
The `o` in this code returns the same value no matter how many properties are accessed or functions are called. For example, `o.a.b.c().d()().asdf()()()() === o` is `true`. Therefore, it allows most state-merging functions composed of selectors and simple methods like filter/map/reduce to execute without issues. However, it's not a silver bullet, so some caution is needed, and it should preferably be used only for state merging.
|
|
250
250
|
|
|
251
251
|
## In-Depth
|
|
252
252
|
|
package/dist/index.browser.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var s=function(){};s.prototype.get=function(){if(this.r||(h(this),l(this)),this.state.promise)throw this.state.promise;if(this.state.error)throw this.state.error;return this.state.value};s.prototype.watch=function(e){return this.r||S(this),(this.l??=new Set).add(e),()=>{this.l.delete(e),this.l.size||l(this)}};s.prototype.subscribe=function(e){let t={f:e,V:{get signal(){return(t.t??=P()).signal}}};if(!this.r)S(this);else if(!this.state.promise&&!this.state.error)try{e(this.state.value,t.V)}catch(o){console.error(o)}return(this.s??=new Set).add(t),()=>{this.s.delete(t),t.t&&(t.t.abort(),t.t=void 0),this.s.size||l(this)}};s.prototype[Symbol.toPrimitive]=function(){return this.state.value};var u=function(e,t){this.c=t?.equals,this.state={promise:void 0,error:void 0,value:e},this.state.value=this.a=e};u.prototype.set=function(e){let t=e instanceof Function?e(this.a):e;y(t,this.state.value,this.c)||(this.a=t,A(this))};u.prototype.m=!0;u.prototype.r=!0;u.prototype.o=!1;Object.setPrototypeOf(u.prototype,s.prototype);var p=function(e,t){this.y=e,this.c=t?.equals,this.b=t?.persist,this.state={promise:v,error:void 0,value:void 0};let o=this;this.V={get signal(){return(o.t??=P()).signal}}};p.prototype.m=!1;p.prototype.r=!1;p.prototype.o=!1;p.prototype.i=0;Object.setPrototypeOf(p.prototype,s.prototype);var c=()=>c,g=()=>{};Object.setPrototypeOf(c,new Proxy(c,{get:(e,t)=>t===Symbol.toPrimitive?g:c}));var v=Promise.resolve(),k=(e,t)=>e instanceof Function?new p(e,t):new u(e,t),O=e=>k((t,o)=>{let n,r,m=e(x=>{try{return t(x)}catch(V){if(!V)throw V;b(V)?(n??=[]).push(V):r=V}return c},o);if(r)throw r;if(n)throw Promise.all(n);return m}),f=!1,a=[],S=e=>{e.u||(e.u=!0,A(e))},A=e=>{e.o||(e.o=!0,a.push(e),f||(f=!0,queueMicrotask(_)))},_=()=>{f=!1;{let t=a;a=[];for(let o of t)o.state.promise=void 0,o.state.error=o.A,o.state.value=o.a,I(o)}let e=a;a=[];for(let t=e.length;t--;){let o=e[t];o.d=!1,o.u&&(o.o=!0,h(o)),o.o&&w(o)}},w=e=>{if(e.o=!1,e.n)for(let t of e.n)t.u=!0;if(e.l)for(let t of e.l)t();if(e.s&&!e.state.promise&&!e.state.error)for(let t of e.s){t.t&&(t.t.abort(),t.t=void 0);try{t.f(e.state.value,t.V)}catch(o){console.error(o)}}},I=e=>{if(!e.d){if(e.d=!0,e.n)for(let t of e.n)I(t);a.push(e)}},i=class{e;constructor(t){this.e=t}},h=e=>{let t=++e.i;e.r=!0,e.u=!1,e.state.promise=void 0,e.t&&(e.t.abort(),e.t=void 0);let o=e.p;o&&(e.p=new Set);try{let n=e.y((r,m=!0)=>{if(t!==e.i)throw void 0;if(e!==r&&(r.r||(h(r),r.o&&(r.o=!1,w(r))),o?.delete(r),(e.p??=new Set).add(r),(r.n??=new Set).add(e)),!m)return r.state;if(r.state.promise)throw new i(r.state.promise);if(r.state.error)throw new i(r.state.error);return r.state.value},e.V);b(n)?(e.state.promise=n,n.then(r=>{t===e.i&&(y(r,e.state.value,e.c)?e.state.promise=void 0:(e.a=r,e.A=void 0,A(e)))},r=>{t===e.i&&(r instanceof Promise?e.state.promise=void 0:(r instanceof i?r=r.e:console.error(r),e.A=r,A(e)))})):(++e.i,e.state.error=void 0,y(n,e.state.value,e.c)?e.o=!1:e.state.value=e.a=n)}catch(n){++e.i,n?(n instanceof i?n=n.e:console.error(n),b(n)?e.state.promise=n:e.state.error=n):e.o=!1}if(o)for(let n of o)n.n.delete(e),l(n)},d=!1,l=e=>{if(!e.m&&!e.b&&!e.n?.size&&!e.l?.size&&!e.s?.size){if(!d){setTimeout(()=>{d=!0,l(e),d=!1},0);return}if(e.state.promise=v,e.a=e.A=e.state.error=e.state.value=void 0,e.o=e.u=e.r=!1,e.t&&(e.t.abort(),e.t=void 0),e.p){for(let t of e.p)t.n.delete(e),l(t);e.p.clear()}}},y=(e,t,o)=>Object.is(e,t)||o!==void 0&&t!==void 0&&o(e,t),b=e=>typeof e?.then=="function",P=()=>{let e=new AbortController,t=e.signal,o=new Promise(n=>{t.then=r=>o.then(r),t.addEventListener("abort",n,{once:!0,passive:!0})});return{abort:()=>e.abort(),signal:t}};export{k as $,O as $$,v as inactive};
|
package/dist/index.js
CHANGED
|
@@ -273,12 +273,17 @@ const execute = (atom) => {
|
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
},
|
|
276
|
-
(
|
|
276
|
+
(e) => {
|
|
277
277
|
if (counter === atom.g) {
|
|
278
|
-
if (
|
|
278
|
+
if (e instanceof Promise) {
|
|
279
279
|
atom.state.promise = void 0;
|
|
280
280
|
} else {
|
|
281
|
-
|
|
281
|
+
if (e instanceof Wrapped) {
|
|
282
|
+
e = e.e;
|
|
283
|
+
} else {
|
|
284
|
+
console.error(e);
|
|
285
|
+
}
|
|
286
|
+
atom.n = e;
|
|
282
287
|
requestPropagate(atom);
|
|
283
288
|
}
|
|
284
289
|
}
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Atom, DerivedAtom, PrimitiveAtom } from '.';
|
|
2
|
-
export * from '.';
|
|
3
2
|
export declare const useAtom: <Value>(atom: PrimitiveAtom<Value>) => readonly [Value, (value: import(".").AtomUpdater<Value>) => void];
|
|
4
3
|
export declare const useAtomValue: <Value>(atom: Atom<Value>) => Value;
|
|
5
4
|
export declare const useAtomState: <Value>(atom: DerivedAtom<Value>) => import(".").AtomState<Value>;
|
package/dist/react.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useState, useSyncExternalStore } from "react";
|
|
2
|
-
export * from ".";
|
|
3
2
|
const useAtom = (atom) => [useSyncExternalStore(atom.watch, atom.get), atom.set];
|
|
4
3
|
const useAtomValue = (atom) => useSyncExternalStore(atom.watch, atom.get);
|
|
5
4
|
const useAtomState = (atom) => useSyncExternalStore(atom.watch, () => {
|
package/package.json
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bansa",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./index.js",
|
|
7
|
-
"types": "./index.d.ts",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
"./package.json": "./package.json",
|
|
10
|
+
"./react": {
|
|
11
|
+
"types": "./dist/react.d.ts",
|
|
12
|
+
"default": "./dist/react.js"
|
|
13
|
+
},
|
|
10
14
|
".": {
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
"default": "./dist/react.js"
|
|
14
|
-
},
|
|
15
|
-
"default": {
|
|
16
|
-
"types": "./dist/index.d.ts",
|
|
17
|
-
"default": "./dist/index.js"
|
|
18
|
-
}
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
19
17
|
}
|
|
20
18
|
},
|
|
21
19
|
"devDependencies": {
|
package/src/index.ts
DELETED
|
@@ -1,604 +0,0 @@
|
|
|
1
|
-
export type Atom<Value> = PrimitiveAtom<Value> | DerivedAtom<Value>;
|
|
2
|
-
export type CommonAtom<Value> = {
|
|
3
|
-
readonly get: () => Value;
|
|
4
|
-
readonly watch: (watcher: AtomWatcher) => () => void;
|
|
5
|
-
readonly subscribe: (subscriber: AtomSubscribe<Value>) => () => void;
|
|
6
|
-
readonly state: AtomState<Value>;
|
|
7
|
-
};
|
|
8
|
-
export type PrimitiveAtom<Value> = CommonAtom<Value> & {
|
|
9
|
-
readonly set: (value: AtomUpdater<Value>) => void;
|
|
10
|
-
readonly state: AtomSuccessState<Value>;
|
|
11
|
-
};
|
|
12
|
-
export type DerivedAtom<Value> = CommonAtom<Value>;
|
|
13
|
-
|
|
14
|
-
export type AtomWatcher = () => void;
|
|
15
|
-
export type AtomSubscribe<Value> = (
|
|
16
|
-
value: Value,
|
|
17
|
-
options: AtomSubscriberOptions,
|
|
18
|
-
) => void;
|
|
19
|
-
export type AtomInit<Value> = Value | AtomGetter<Value>;
|
|
20
|
-
export type AtomUpdater<Value> = Value | AtomReducer<Value>;
|
|
21
|
-
// TODO: readonly
|
|
22
|
-
export type AtomInactiveState<Value> = {
|
|
23
|
-
promise: typeof inactive;
|
|
24
|
-
error: any;
|
|
25
|
-
value?: Value;
|
|
26
|
-
};
|
|
27
|
-
export type AtomPromiseState<Value> = {
|
|
28
|
-
promise: PromiseLike<Value>;
|
|
29
|
-
error: any;
|
|
30
|
-
value?: Value;
|
|
31
|
-
};
|
|
32
|
-
export type AtomSuccessState<Value> = {
|
|
33
|
-
promise: undefined;
|
|
34
|
-
error: undefined;
|
|
35
|
-
value: Value;
|
|
36
|
-
};
|
|
37
|
-
export type AtomErrorState<Value> = {
|
|
38
|
-
promise: undefined;
|
|
39
|
-
error: any;
|
|
40
|
-
value?: Value;
|
|
41
|
-
};
|
|
42
|
-
export type AtomState<Value> =
|
|
43
|
-
| AtomInactiveState<Value>
|
|
44
|
-
| AtomPromiseState<Value>
|
|
45
|
-
| AtomSuccessState<Value>
|
|
46
|
-
| AtomErrorState<Value>;
|
|
47
|
-
|
|
48
|
-
export type AtomSubscriberOptions = { readonly signal: ThenableSignal };
|
|
49
|
-
export type AtomGetter<Value> = (
|
|
50
|
-
get: GetAtom,
|
|
51
|
-
options: AtomGetOptions,
|
|
52
|
-
) => Value | PromiseLike<Value>;
|
|
53
|
-
export type AtomReducer<Value> = (value: Value) => Value;
|
|
54
|
-
|
|
55
|
-
export type AtomGetOptions = { readonly signal: ThenableSignal };
|
|
56
|
-
export type ThenableSignal = AbortSignal & { then: (f: () => void) => void };
|
|
57
|
-
type ThenableSignalController = {
|
|
58
|
-
abort: () => void;
|
|
59
|
-
signal: ThenableSignal;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export type GetAtom = {
|
|
63
|
-
<Value>(anotherAtom: Atom<Value>, unwrap?: true): Value;
|
|
64
|
-
<Value>(anotherAtom: Atom<Value>, unwrap: false): AtomState<Value>;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
type CreateAtom = {
|
|
68
|
-
<Value>(
|
|
69
|
-
init: AtomGetter<Value>,
|
|
70
|
-
options?: AtomOptions<Value>,
|
|
71
|
-
): DerivedAtom<Value>;
|
|
72
|
-
<Value>(init: Value, options?: AtomOptions<Value>): PrimitiveAtom<Value>;
|
|
73
|
-
<Value>(
|
|
74
|
-
init: Value | AtomGetter<Value>,
|
|
75
|
-
options?: AtomOptions<Value>,
|
|
76
|
-
): Atom<Value>;
|
|
77
|
-
};
|
|
78
|
-
export type AtomOptions<Value> = {
|
|
79
|
-
equals?: AtomEquals<Value>;
|
|
80
|
-
persist?: boolean;
|
|
81
|
-
eager?: boolean;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
export type AtomEquals<Value> = (value: Value, prevValue: Value) => boolean;
|
|
85
|
-
export type AtomScope = {
|
|
86
|
-
<Value>(baseAtom: PrimitiveAtom<Value>): PrimitiveAtom<Value>;
|
|
87
|
-
<Value>(baseAtom: DerivedAtom<Value>): DerivedAtom<Value>;
|
|
88
|
-
<Value>(baseAtom: Atom<Value>): Atom<Value>;
|
|
89
|
-
} & {
|
|
90
|
-
get: {
|
|
91
|
-
<Value>(baseAtom: PrimitiveAtom<Value>): PrimitiveAtom<Value>;
|
|
92
|
-
<Value>(baseAtom: DerivedAtom<Value>): DerivedAtom<Value>;
|
|
93
|
-
<Value>(baseAtom: Atom<Value>): Atom<Value>;
|
|
94
|
-
};
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export type SetLike<Key> =
|
|
98
|
-
| Key[]
|
|
99
|
-
| Set<Key>
|
|
100
|
-
| (Key extends object ? WeakSet<Key> : never);
|
|
101
|
-
export type MapLike<Key, Value> =
|
|
102
|
-
| Map<Key, Value>
|
|
103
|
-
| (Key extends object ? WeakMap<Key, Value> : never)
|
|
104
|
-
| (Key extends string | number | symbol ? Record<Key, Value> : never);
|
|
105
|
-
|
|
106
|
-
type AtomInternal<Value> =
|
|
107
|
-
| PrimitiveAtomInternal<Value>
|
|
108
|
-
| DerivedAtomInternal<Value>;
|
|
109
|
-
type CommonAtomInternal<Value> = {
|
|
110
|
-
_equals?: AtomEquals<Value>;
|
|
111
|
-
|
|
112
|
-
_marked: boolean;
|
|
113
|
-
_nextError?: any;
|
|
114
|
-
_nextValue?: Value;
|
|
115
|
-
_children?: Set<DerivedAtomInternal<any>>;
|
|
116
|
-
_watchers?: Set<AtomWatcher>;
|
|
117
|
-
_subscribers?: Set<AtomSubscribeInternal<Value>>;
|
|
118
|
-
};
|
|
119
|
-
type PrimitiveAtomInternal<Value> = CommonAtomInternal<Value> &
|
|
120
|
-
PrimitiveAtom<Value> & {
|
|
121
|
-
readonly _source: true;
|
|
122
|
-
readonly _active: true;
|
|
123
|
-
_needPropagate: boolean;
|
|
124
|
-
|
|
125
|
-
// _init: Value;
|
|
126
|
-
};
|
|
127
|
-
type DerivedAtomInternal<Value> = CommonAtomInternal<Value> &
|
|
128
|
-
DerivedAtom<Value> & {
|
|
129
|
-
readonly _source: false;
|
|
130
|
-
readonly _persist: boolean;
|
|
131
|
-
_active: boolean;
|
|
132
|
-
_needExecute: boolean;
|
|
133
|
-
_needPropagate: boolean;
|
|
134
|
-
|
|
135
|
-
_init: AtomGetterInternal<Value>;
|
|
136
|
-
_options: AtomGetOptions;
|
|
137
|
-
_counter: number;
|
|
138
|
-
_ctrl?: ThenableSignalController;
|
|
139
|
-
_dependencies?: Set<AtomInternal<any>>;
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
type GetAtomInternal = {
|
|
143
|
-
<Value>(anotherAtom: AtomInternal<Value>, unwrap?: true): Value;
|
|
144
|
-
<Value>(anotherAtom: AtomInternal<Value>, unwrap: false): AtomState<Value>;
|
|
145
|
-
};
|
|
146
|
-
type AtomGetterInternal<Value> = (
|
|
147
|
-
get: GetAtomInternal,
|
|
148
|
-
options: AtomGetOptions,
|
|
149
|
-
) => Value | PromiseLike<Value>;
|
|
150
|
-
type AtomSubscribeInternal<Value> = {
|
|
151
|
-
_subscriber: AtomSubscribe<Value>;
|
|
152
|
-
_options: AtomSubscriberOptions;
|
|
153
|
-
_ctrl?: ThenableSignalController;
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
// JS에서 자료형을 만드는 방법은 여러 가지가 있다:
|
|
157
|
-
// 클로저를 활용해서 익명 함수로 메서드가 구현된 객체 반환하기
|
|
158
|
-
// -> 제일 쉽고 직관적이지만 공통 메서드/멤버 변수가 매번 새로 선언되므로 시간/메모리 측면에서 비효율적이다.
|
|
159
|
-
// 공통 메서드/멤버 변수만 별도의 객체로 추출한 뒤 Object.create로 프로토타입 설정하기
|
|
160
|
-
// -> Object.create가 new보다 2~3배 느린 걸로 보인다. 프로토타입 직접 건드리는 거라 그런가...
|
|
161
|
-
// class 쓰기
|
|
162
|
-
// -> 공통 멤버 변수를 프로토타입에 박을 방법이 없다.
|
|
163
|
-
// function 쓰기
|
|
164
|
-
// -> 그나마 최선의 해결책.
|
|
165
|
-
|
|
166
|
-
const AtomPrototype = function <Value>(
|
|
167
|
-
this: AtomInternal<Value>,
|
|
168
|
-
) {} as unknown as { new <_Value>(): AtomInternal<_Value> };
|
|
169
|
-
AtomPrototype.prototype.get = function <Value>(this: AtomInternal<Value>) {
|
|
170
|
-
if (!this._active) {
|
|
171
|
-
execute(this);
|
|
172
|
-
disableAtom(this);
|
|
173
|
-
}
|
|
174
|
-
if (this.state.promise) throw this.state.promise;
|
|
175
|
-
if (this.state.error) throw this.state.error;
|
|
176
|
-
return this.state.value!;
|
|
177
|
-
};
|
|
178
|
-
AtomPrototype.prototype.watch = function <Value>(
|
|
179
|
-
this: AtomInternal<Value>,
|
|
180
|
-
watcher: AtomWatcher,
|
|
181
|
-
) {
|
|
182
|
-
if (!this._active) {
|
|
183
|
-
requestActivate(this);
|
|
184
|
-
}
|
|
185
|
-
(this._watchers ??= new Set()).add(watcher);
|
|
186
|
-
return () => {
|
|
187
|
-
this._watchers!.delete(watcher);
|
|
188
|
-
if (!this._watchers!.size) {
|
|
189
|
-
disableAtom(this);
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
};
|
|
193
|
-
AtomPrototype.prototype.subscribe = function <Value>(
|
|
194
|
-
this: AtomInternal<Value>,
|
|
195
|
-
subscriber: AtomSubscribe<unknown>,
|
|
196
|
-
) {
|
|
197
|
-
const atomSubscriber: AtomSubscribeInternal<unknown> = {
|
|
198
|
-
_subscriber: subscriber,
|
|
199
|
-
_options: {
|
|
200
|
-
get signal() {
|
|
201
|
-
return (atomSubscriber._ctrl ??= createThenableSignal()).signal;
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
};
|
|
205
|
-
if (!this._active) {
|
|
206
|
-
requestActivate(this);
|
|
207
|
-
} else if (!this.state.promise && !this.state.error) {
|
|
208
|
-
try {
|
|
209
|
-
subscriber(this.state.value!, atomSubscriber._options);
|
|
210
|
-
} catch (e) {
|
|
211
|
-
console.error(e);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
(this._subscribers ??= new Set()).add(atomSubscriber);
|
|
215
|
-
return () => {
|
|
216
|
-
this._subscribers!.delete(atomSubscriber);
|
|
217
|
-
if (atomSubscriber._ctrl) {
|
|
218
|
-
atomSubscriber._ctrl.abort();
|
|
219
|
-
atomSubscriber._ctrl = undefined;
|
|
220
|
-
}
|
|
221
|
-
if (!this._subscribers!.size) {
|
|
222
|
-
disableAtom(this);
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
};
|
|
226
|
-
AtomPrototype.prototype[Symbol.toPrimitive] = function <Value>(
|
|
227
|
-
this: AtomInternal<Value>,
|
|
228
|
-
) {
|
|
229
|
-
return this.state.value;
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
const PrimitiveAtomPrototype = function <Value>(
|
|
233
|
-
this: PrimitiveAtomInternal<Value>,
|
|
234
|
-
init: Value,
|
|
235
|
-
options?: AtomOptions<Value>,
|
|
236
|
-
) {
|
|
237
|
-
// this._init = init;
|
|
238
|
-
this._equals = options?.equals;
|
|
239
|
-
|
|
240
|
-
(this as any).state = {
|
|
241
|
-
promise: undefined,
|
|
242
|
-
error: undefined,
|
|
243
|
-
value: init,
|
|
244
|
-
};
|
|
245
|
-
this.state.value = this._nextValue = init;
|
|
246
|
-
} as unknown as {
|
|
247
|
-
new <_Value>(
|
|
248
|
-
init: _Value,
|
|
249
|
-
options?: AtomOptions<_Value>,
|
|
250
|
-
): PrimitiveAtomInternal<_Value>;
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
PrimitiveAtomPrototype.prototype.set = function <Value>(
|
|
254
|
-
this: PrimitiveAtomInternal<Value>,
|
|
255
|
-
value: AtomUpdater<unknown>,
|
|
256
|
-
) {
|
|
257
|
-
const nextValue =
|
|
258
|
-
value instanceof Function ? value(this._nextValue!) : value;
|
|
259
|
-
if (!equals(nextValue, this.state.value, this._equals)) {
|
|
260
|
-
this._nextValue = nextValue;
|
|
261
|
-
requestPropagate(this);
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
PrimitiveAtomPrototype.prototype._source = true;
|
|
265
|
-
PrimitiveAtomPrototype.prototype._active = true;
|
|
266
|
-
PrimitiveAtomPrototype.prototype._needPropagate = false;
|
|
267
|
-
Object.setPrototypeOf(
|
|
268
|
-
PrimitiveAtomPrototype.prototype,
|
|
269
|
-
AtomPrototype.prototype,
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
const DerivedAtomPrototype = function <Value>(
|
|
273
|
-
this: DerivedAtomInternal<Value>,
|
|
274
|
-
init: AtomGetter<Value>,
|
|
275
|
-
options?: AtomOptions<Value>,
|
|
276
|
-
) {
|
|
277
|
-
this._init = init as AtomGetterInternal<Value>;
|
|
278
|
-
this._equals = options?.equals;
|
|
279
|
-
(this as any)._persist = options?.persist;
|
|
280
|
-
(this as any).state = {
|
|
281
|
-
promise: inactive,
|
|
282
|
-
error: undefined,
|
|
283
|
-
value: undefined,
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
const self = this;
|
|
287
|
-
this._options = {
|
|
288
|
-
get signal() {
|
|
289
|
-
return (self._ctrl ??= createThenableSignal()).signal;
|
|
290
|
-
},
|
|
291
|
-
};
|
|
292
|
-
} as unknown as {
|
|
293
|
-
new <Value>(
|
|
294
|
-
init: AtomGetter<Value>,
|
|
295
|
-
options?: AtomOptions<Value>,
|
|
296
|
-
): DerivedAtomInternal<Value>;
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
DerivedAtomPrototype.prototype._source = false;
|
|
300
|
-
DerivedAtomPrototype.prototype._active = false;
|
|
301
|
-
DerivedAtomPrototype.prototype._needPropagate = false;
|
|
302
|
-
DerivedAtomPrototype.prototype._counter = 0;
|
|
303
|
-
Object.setPrototypeOf(DerivedAtomPrototype.prototype, AtomPrototype.prototype);
|
|
304
|
-
|
|
305
|
-
const ouroboros: any = () => ouroboros;
|
|
306
|
-
const toUndefined = () => undefined;
|
|
307
|
-
Object.setPrototypeOf(
|
|
308
|
-
ouroboros,
|
|
309
|
-
new Proxy(ouroboros, {
|
|
310
|
-
get: (_, k) => (k === Symbol.toPrimitive ? toUndefined : ouroboros),
|
|
311
|
-
}),
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
export const inactive = Promise.resolve();
|
|
315
|
-
export const $: CreateAtom = <Value>(
|
|
316
|
-
init: Value | AtomGetter<Value>,
|
|
317
|
-
options?: AtomOptions<Value>,
|
|
318
|
-
) => {
|
|
319
|
-
if (init instanceof Function)
|
|
320
|
-
return new DerivedAtomPrototype(init, options);
|
|
321
|
-
return new PrimitiveAtomPrototype(init, options) as any;
|
|
322
|
-
};
|
|
323
|
-
export const $$ = <Value>(init: AtomGetter<Value>) =>
|
|
324
|
-
$((get, options) => {
|
|
325
|
-
let promises: PromiseLike<Value>[] | undefined;
|
|
326
|
-
let error: unknown;
|
|
327
|
-
const result = init((atom) => {
|
|
328
|
-
try {
|
|
329
|
-
return get(atom);
|
|
330
|
-
} catch (e) {
|
|
331
|
-
if (!e) {
|
|
332
|
-
throw e;
|
|
333
|
-
}
|
|
334
|
-
if (isPromiseLike(e)) {
|
|
335
|
-
(promises ??= []).push(e as PromiseLike<Value>);
|
|
336
|
-
} else {
|
|
337
|
-
error = e;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
return ouroboros;
|
|
341
|
-
}, options);
|
|
342
|
-
if (error) throw error;
|
|
343
|
-
if (promises) throw Promise.all(promises);
|
|
344
|
-
return result;
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
let pendingUpdateAtoms = false;
|
|
348
|
-
let stack: AtomInternal<any>[] = [];
|
|
349
|
-
const requestActivate = <Value>(atom: DerivedAtomInternal<Value>) => {
|
|
350
|
-
if (!atom._needExecute) {
|
|
351
|
-
atom._needExecute = true;
|
|
352
|
-
requestPropagate(atom);
|
|
353
|
-
}
|
|
354
|
-
};
|
|
355
|
-
const requestPropagate = <Value>(atom: AtomInternal<Value>) => {
|
|
356
|
-
if (!atom._needPropagate) {
|
|
357
|
-
atom._needPropagate = true;
|
|
358
|
-
stack.push(atom);
|
|
359
|
-
if (!pendingUpdateAtoms) {
|
|
360
|
-
pendingUpdateAtoms = true;
|
|
361
|
-
queueMicrotask(updateAtoms);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
};
|
|
365
|
-
const updateAtoms = () => {
|
|
366
|
-
pendingUpdateAtoms = false;
|
|
367
|
-
{
|
|
368
|
-
const updatedAtoms = stack;
|
|
369
|
-
stack = [];
|
|
370
|
-
for (const atom of updatedAtoms) {
|
|
371
|
-
atom.state.promise = undefined;
|
|
372
|
-
atom.state.error = atom._nextError;
|
|
373
|
-
atom.state.value = atom._nextValue;
|
|
374
|
-
mark(atom);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
const markedAtoms = stack as DerivedAtomInternal<any>[];
|
|
378
|
-
stack = [];
|
|
379
|
-
for (let i = markedAtoms.length; i--; ) {
|
|
380
|
-
const atom = markedAtoms[i];
|
|
381
|
-
atom._marked = false;
|
|
382
|
-
if (atom._needExecute) {
|
|
383
|
-
atom._needPropagate = true;
|
|
384
|
-
execute(atom);
|
|
385
|
-
}
|
|
386
|
-
if (atom._needPropagate) {
|
|
387
|
-
propagate(atom);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
const propagate = <Value>(atom: AtomInternal<Value>) => {
|
|
392
|
-
atom._needPropagate = false;
|
|
393
|
-
if (atom._children) {
|
|
394
|
-
for (const child of atom._children) {
|
|
395
|
-
child._needExecute = true;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
if (atom._watchers) {
|
|
399
|
-
for (const watcher of atom._watchers) {
|
|
400
|
-
watcher();
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
if (atom._subscribers && !atom.state.promise && !atom.state.error) {
|
|
404
|
-
for (const subscriber of atom._subscribers) {
|
|
405
|
-
if (subscriber._ctrl) {
|
|
406
|
-
subscriber._ctrl.abort();
|
|
407
|
-
subscriber._ctrl = undefined;
|
|
408
|
-
}
|
|
409
|
-
try {
|
|
410
|
-
subscriber._subscriber(atom.state.value!, subscriber._options);
|
|
411
|
-
} catch (e) {
|
|
412
|
-
console.error(e);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
};
|
|
417
|
-
const mark = (atom: AtomInternal<any>) => {
|
|
418
|
-
if (!atom._marked) {
|
|
419
|
-
atom._marked = true;
|
|
420
|
-
if (atom._children) {
|
|
421
|
-
for (const child of atom._children) {
|
|
422
|
-
mark(child);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
stack.push(atom);
|
|
426
|
-
}
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
class Wrapped {
|
|
430
|
-
e: unknown;
|
|
431
|
-
constructor(e: unknown) {
|
|
432
|
-
this.e = e;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
const execute = <Value>(atom: DerivedAtomInternal<Value>) => {
|
|
436
|
-
const counter = ++atom._counter;
|
|
437
|
-
atom._active = true;
|
|
438
|
-
atom._needExecute = false;
|
|
439
|
-
atom.state.promise = undefined;
|
|
440
|
-
|
|
441
|
-
if (atom._ctrl) {
|
|
442
|
-
atom._ctrl.abort();
|
|
443
|
-
atom._ctrl = undefined;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// TODO: nextDependencies
|
|
447
|
-
const oldDependencies = atom._dependencies;
|
|
448
|
-
if (oldDependencies) {
|
|
449
|
-
atom._dependencies = new Set();
|
|
450
|
-
}
|
|
451
|
-
try {
|
|
452
|
-
const value = atom._init(
|
|
453
|
-
<V>(anotherAtom: AtomInternal<V>, unwrap = true) => {
|
|
454
|
-
if (counter !== atom._counter) throw undefined;
|
|
455
|
-
if ((atom as unknown) !== anotherAtom) {
|
|
456
|
-
if (!anotherAtom._active) {
|
|
457
|
-
execute(anotherAtom);
|
|
458
|
-
if (anotherAtom._needPropagate) {
|
|
459
|
-
anotherAtom._needPropagate = false;
|
|
460
|
-
propagate(anotherAtom);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
oldDependencies?.delete(anotherAtom);
|
|
464
|
-
(atom._dependencies ??= new Set()).add(anotherAtom);
|
|
465
|
-
(anotherAtom._children ??= new Set()).add(atom);
|
|
466
|
-
}
|
|
467
|
-
if (!unwrap) return anotherAtom.state;
|
|
468
|
-
if (anotherAtom.state.promise)
|
|
469
|
-
throw new Wrapped(anotherAtom.state.promise);
|
|
470
|
-
if (anotherAtom.state.error)
|
|
471
|
-
throw new Wrapped(anotherAtom.state.error);
|
|
472
|
-
return anotherAtom.state.value as V;
|
|
473
|
-
},
|
|
474
|
-
atom._options,
|
|
475
|
-
);
|
|
476
|
-
|
|
477
|
-
if (isPromiseLike(value)) {
|
|
478
|
-
atom.state.promise = value;
|
|
479
|
-
value.then(
|
|
480
|
-
(value) => {
|
|
481
|
-
if (counter === atom._counter) {
|
|
482
|
-
if (equals(value, atom.state.value, atom._equals)) {
|
|
483
|
-
atom.state.promise = undefined;
|
|
484
|
-
// watchers 재실행 해야 할까?
|
|
485
|
-
} else {
|
|
486
|
-
atom._nextValue = value;
|
|
487
|
-
atom._nextError = undefined;
|
|
488
|
-
requestPropagate(atom);
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
},
|
|
492
|
-
(error) => {
|
|
493
|
-
if (counter === atom._counter) {
|
|
494
|
-
if (error instanceof Promise) {
|
|
495
|
-
atom.state.promise = undefined;
|
|
496
|
-
} else {
|
|
497
|
-
atom._nextError = error;
|
|
498
|
-
requestPropagate(atom);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
},
|
|
502
|
-
);
|
|
503
|
-
} else {
|
|
504
|
-
++atom._counter;
|
|
505
|
-
atom.state.error = undefined;
|
|
506
|
-
if (equals(value, atom.state.value, atom._equals)) {
|
|
507
|
-
atom._needPropagate = false;
|
|
508
|
-
} else {
|
|
509
|
-
atom.state.value = atom._nextValue = value;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
} catch (e) {
|
|
513
|
-
++atom._counter;
|
|
514
|
-
if (!e) {
|
|
515
|
-
atom._needPropagate = false;
|
|
516
|
-
} else {
|
|
517
|
-
if (e instanceof Wrapped) {
|
|
518
|
-
e = e.e;
|
|
519
|
-
} else {
|
|
520
|
-
console.error(e);
|
|
521
|
-
}
|
|
522
|
-
if (isPromiseLike(e)) {
|
|
523
|
-
atom.state.promise = e as PromiseLike<Value>;
|
|
524
|
-
} else {
|
|
525
|
-
atom.state.error = e;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
if (oldDependencies) {
|
|
531
|
-
for (const dep of oldDependencies) {
|
|
532
|
-
dep._children!.delete(atom);
|
|
533
|
-
disableAtom(dep);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
// TODO: 좀 대충 짜놨는데 개선할 수 있을지 고민해봐야
|
|
539
|
-
let disabling = false;
|
|
540
|
-
const disableAtom = <Value>(atom: AtomInternal<Value>) => {
|
|
541
|
-
if (
|
|
542
|
-
!atom._source &&
|
|
543
|
-
!atom._persist &&
|
|
544
|
-
!atom._children?.size &&
|
|
545
|
-
!atom._watchers?.size &&
|
|
546
|
-
!atom._subscribers?.size
|
|
547
|
-
) {
|
|
548
|
-
if (!disabling) {
|
|
549
|
-
setTimeout(() => {
|
|
550
|
-
disabling = true;
|
|
551
|
-
disableAtom(atom);
|
|
552
|
-
disabling = false;
|
|
553
|
-
}, 0);
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
atom.state.promise = inactive;
|
|
557
|
-
atom._nextValue =
|
|
558
|
-
atom._nextError =
|
|
559
|
-
atom.state.error =
|
|
560
|
-
atom.state.value =
|
|
561
|
-
undefined;
|
|
562
|
-
atom._needPropagate = atom._needExecute = atom._active = false;
|
|
563
|
-
if (atom._ctrl) {
|
|
564
|
-
atom._ctrl.abort();
|
|
565
|
-
atom._ctrl = undefined;
|
|
566
|
-
}
|
|
567
|
-
if (atom._dependencies) {
|
|
568
|
-
for (const dep of atom._dependencies) {
|
|
569
|
-
dep._children!.delete(atom);
|
|
570
|
-
disableAtom(dep);
|
|
571
|
-
}
|
|
572
|
-
atom._dependencies.clear();
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
};
|
|
576
|
-
|
|
577
|
-
const equals = <Value>(
|
|
578
|
-
value: Value,
|
|
579
|
-
prevValue?: Value,
|
|
580
|
-
equalsFn?: (value: Value, prevValue: Value) => boolean,
|
|
581
|
-
) =>
|
|
582
|
-
Object.is(value, prevValue) ||
|
|
583
|
-
(equalsFn !== undefined &&
|
|
584
|
-
prevValue !== undefined &&
|
|
585
|
-
equalsFn(value, prevValue));
|
|
586
|
-
|
|
587
|
-
const isPromiseLike = (x: unknown): x is PromiseLike<unknown> =>
|
|
588
|
-
typeof (x as PromiseLike<unknown>)?.then === 'function';
|
|
589
|
-
|
|
590
|
-
const createThenableSignal = () => {
|
|
591
|
-
const ctrl = new AbortController();
|
|
592
|
-
const signal = ctrl.signal as ThenableSignal;
|
|
593
|
-
const promise = new Promise((resolve) => {
|
|
594
|
-
signal.then = (f: () => void) => promise.then(f);
|
|
595
|
-
signal.addEventListener('abort', resolve, {
|
|
596
|
-
once: true,
|
|
597
|
-
passive: true,
|
|
598
|
-
});
|
|
599
|
-
});
|
|
600
|
-
return {
|
|
601
|
-
abort: () => ctrl.abort(),
|
|
602
|
-
signal,
|
|
603
|
-
};
|
|
604
|
-
};
|
package/src/react.tsx
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { useState, useSyncExternalStore } from 'react';
|
|
2
|
-
import type { Atom, DerivedAtom, PrimitiveAtom } from '.';
|
|
3
|
-
|
|
4
|
-
export * from '.';
|
|
5
|
-
|
|
6
|
-
export const useAtom = <Value,>(atom: PrimitiveAtom<Value>) =>
|
|
7
|
-
[useSyncExternalStore(atom.watch, atom.get), atom.set] as const;
|
|
8
|
-
|
|
9
|
-
export const useAtomValue = <Value,>(atom: Atom<Value>) =>
|
|
10
|
-
useSyncExternalStore(atom.watch, atom.get);
|
|
11
|
-
|
|
12
|
-
export const useAtomState = <Value,>(atom: DerivedAtom<Value>) =>
|
|
13
|
-
useSyncExternalStore(atom.watch, () => {
|
|
14
|
-
// avoid https://github.com/facebook/react/issues/31730
|
|
15
|
-
try {
|
|
16
|
-
atom.get();
|
|
17
|
-
} catch (_) {}
|
|
18
|
-
return atom.state;
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export const useStateAtom = <Value,>(atom: PrimitiveAtom<Value>) => {
|
|
22
|
-
const [state, setState] = useState(() => atom.get());
|
|
23
|
-
const setStateWithAtom = (newState: Value) => {
|
|
24
|
-
setState(newState);
|
|
25
|
-
atom.set(newState);
|
|
26
|
-
};
|
|
27
|
-
return [state, setStateWithAtom] as const;
|
|
28
|
-
};
|