rask-ui 0.18.2 → 0.19.0

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 CHANGED
@@ -48,7 +48,10 @@ RASK provides a set of reactive hooks for building interactive UIs. These hooks
48
48
  - **`useState`** - Create reactive state objects
49
49
  - **`useEffect`** - Run side effects when dependencies change
50
50
  - **`useDerived`** - Derive values from state with automatic caching
51
- - **`useAsync`** - Manage async operations (fetch, mutations, polling, etc.)
51
+ - **`useAsync`** - Fetch async values with automatic observation and cancellation
52
+ - **`useAction`** - Perform async operations with queuing and retry support
53
+ - **`useSuspend`** - Suspend until multiple async values resolve
54
+ - **`useCatchError`** - Catch and handle component errors (hook-based error boundary)
52
55
  - **`useRouter`** - Type-safe client-side routing
53
56
  - **`createContext`** / **`useContext`** - Share data through the component tree
54
57
  - **`useView`** - Compose state and methods into reusable objects
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,EACL,SAAS,EACT,KAAK,EACL,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAsB,QAAQ,EAAU,MAAM,eAAe,CAAC;AAIrE,MAAM,MAAM,8BAA8B,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAC3D,CAAC,MAAM,KAAK,CAAC,GACb,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC;AAE1B,qBAAa,sBAAuB,SAAQ,SAAS;IAC3C,QAAQ,EAAE,8BAA8B,CAAC,GAAG,CAAC,CAAC;IACtD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAC9B,QAAQ,WAML;IACH,OAAO,CAAC,aAAa,CAAc;IAEnC,qBAAqB,IAAI,OAAO;IAMhC,kBAAkB,IAAI,IAAI;IAG1B,yBAAyB,CAAC,SAAS,EAAE,GAAG,GAAG,IAAI;IAe/C,MAAM;CAOP;AAID,wBAAgB,mBAAmB,2CAElC;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,QAM5C;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,QAMxC;AAED,MAAM,MAAM,6BAA6B,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAC1D,CAAC,MAAM,MAAM,KAAK,CAAC,GACnB,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,KAAK,CAAC,CAAC;AAEhC,qBAAa,qBAAqB,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IACnE,KAAK,EAAE,6BAA6B,CAAC,CAAC,CAAC,CAAC;IAChD,OAAO,CAAC,QAAQ,CAAC,CAAc;IAC/B,OAAO,CAAC,aAAa,CAAC,CAAa;IAwBnC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAE9B,QAAQ,WAOL;IAEH,WAAW,UAAS;IACpB,OAAO,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC,CAAM;IAC3D,QAAQ,gBAAa;IACrB,eAAe;IAUf,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IACjC,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IAEnC,iBAAiB,IAAI,IAAI;IAGzB,oBAAoB,IAAI,IAAI;IAI5B,yBAAyB,CACvB,SAAS,EAAE,QAAQ,CAAC;QAAE,QAAQ,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,CAAC,CAAC,GAClD,IAAI;IAcP,qBAAqB,IAAI,OAAO;IAMhC,MAAM;CAyCP;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,SAO9D"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,EACL,SAAS,EACT,KAAK,EACL,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAsB,QAAQ,EAAU,MAAM,eAAe,CAAC;AAIrE,MAAM,MAAM,8BAA8B,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAC3D,CAAC,MAAM,KAAK,CAAC,GACb,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC;AAE1B,qBAAa,sBAAuB,SAAQ,SAAS;IAC3C,QAAQ,EAAE,8BAA8B,CAAC,GAAG,CAAC,CAAC;IACtD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAC9B,QAAQ,WAML;IACH,OAAO,CAAC,aAAa,CAAc;IAEnC,qBAAqB,IAAI,OAAO;IAMhC,kBAAkB,IAAI,IAAI;IAG1B,yBAAyB,CAAC,SAAS,EAAE,GAAG,GAAG,IAAI;IAe/C,MAAM;CAOP;AAID,wBAAgB,mBAAmB,2CAElC;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,QAM5C;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,QAMxC;AAED,MAAM,MAAM,6BAA6B,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAC1D,CAAC,MAAM,MAAM,KAAK,CAAC,GACnB,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,KAAK,CAAC,CAAC;AAEhC,qBAAa,qBAAqB,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IACnE,KAAK,EAAE,6BAA6B,CAAC,CAAC,CAAC,CAAC;IAChD,OAAO,CAAC,QAAQ,CAAC,CAAc;IAC/B,OAAO,CAAC,aAAa,CAAC,CAAa;IAwBnC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAE9B,QAAQ,WAOL;IAEH,WAAW,UAAS;IACpB,OAAO,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC,CAAM;IAC3D,QAAQ,gBAAa;IACrB,eAAe;IAUf,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IACjC,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IAEnC,iBAAiB,IAAI,IAAI;IAGzB,oBAAoB,IAAI,IAAI;IAI5B,yBAAyB,CACvB,SAAS,EAAE,QAAQ,CAAC;QAAE,QAAQ,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,CAAC,CAAC,GAClD,IAAI;IAcP,qBAAqB,IAAI,OAAO;IAMhC,MAAM;CA6CP;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,SAO9D"}
package/dist/component.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createComponentVNode, Component, } from "inferno";
2
2
  import { getCurrentObserver, Observer, Signal } from "./observation";
3
3
  import { syncBatch } from "./batch";
4
- import { PROXY_MARKER } from "./useState";
4
+ import { CatchErrorContext } from "./useCatchError";
5
5
  export class RaskStatelessComponent extends Component {
6
6
  isNotified = false;
7
7
  isReconciling = false;
@@ -158,10 +158,12 @@ export class RaskStatefulComponent extends Component {
158
158
  this.isRendering = false;
159
159
  }
160
160
  catch (error) {
161
- if (typeof this.context.notifyError !== "function") {
161
+ const notifyError = this.contexts.get(CatchErrorContext) ||
162
+ this.context.getContext?.(CatchErrorContext);
163
+ if (typeof notifyError !== "function") {
162
164
  throw error;
163
165
  }
164
- this.context.notifyError(error);
166
+ notifyError(error);
165
167
  }
166
168
  finally {
167
169
  stopObserving();
@@ -183,12 +185,6 @@ function createReactiveProps(comp) {
183
185
  reactiveProps[prop] = value;
184
186
  continue;
185
187
  }
186
- // Skip objects/arrays - they're already reactive if they're proxies
187
- // No need to wrap them in additional signals
188
- if (typeof value === "object" && value !== null && PROXY_MARKER in value) {
189
- reactiveProps[prop] = value;
190
- continue;
191
- }
192
188
  // Only create reactive getters for primitives
193
189
  Object.defineProperty(reactiveProps, prop, {
194
190
  enumerable: true,
package/dist/error.d.ts CHANGED
@@ -1,16 +1,5 @@
1
- import { Component, VNode } from "inferno";
2
- export declare class ErrorBoundary extends Component<{
3
- children: any;
4
- error: (error: unknown) => VNode;
5
- }, {
1
+ export declare const CatchErrorContext: import("./createContext").Context<(error: unknown) => void>;
2
+ export declare function useCatchError(): {
6
3
  error: unknown;
7
- }> {
8
- getChildContext(): {
9
- notifyError: (error: unknown) => void;
10
- };
11
- state: {
12
- error: null;
13
- };
14
- render(): any;
15
- }
4
+ };
16
5
  //# sourceMappingURL=error.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAE3C,qBAAa,aAAc,SAAQ,SAAS,CAC1C;IAAE,QAAQ,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAAA;CAAE,EACnD;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CACnB;IACC,eAAe;6BAEU,OAAO;;IAKhC,KAAK;;MAAmB;IAExB,MAAM;CAOP"}
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB,4CAAyB,OAAO,KAAK,IAAI,CAAG,CAAC;AAE3E,wBAAgB,aAAa;WAQK,OAAO;EAOxC"}
package/dist/error.js CHANGED
@@ -1,17 +1,16 @@
1
- import { Component } from "inferno";
2
- export class ErrorBoundary extends Component {
3
- getChildContext() {
4
- return {
5
- notifyError: (error) => {
6
- this.setState({ error });
7
- },
8
- };
9
- }
10
- state = { error: null };
11
- render() {
12
- if (this.state.error) {
13
- return this.props.error(this.state.error);
14
- }
15
- return this.props.children;
1
+ import { useState } from "./useState";
2
+ import { createContext, useInjectContext } from "./createContext";
3
+ import { getCurrentComponent } from "./component";
4
+ export const CatchErrorContext = createContext();
5
+ export function useCatchError() {
6
+ const currentComponent = getCurrentComponent();
7
+ if (!currentComponent || currentComponent.isRendering) {
8
+ throw new Error("Only use the useCatchError hook in setup");
16
9
  }
10
+ const inject = useInjectContext(CatchErrorContext);
11
+ const state = useState({
12
+ error: null,
13
+ });
14
+ inject((error) => (state.error = error));
15
+ return state;
17
16
  }
package/dist/index.d.ts CHANGED
@@ -3,7 +3,9 @@ export { useCleanup, useMountEffect, RaskStatefulComponent, RaskStatelessCompone
3
3
  export { createContext, useContext, useInjectContext } from "./createContext";
4
4
  export { useState, assignState } from "./useState";
5
5
  export { useAsync, Async } from "./useAsync";
6
- export { ErrorBoundary } from "./error";
6
+ export { useAction, Action } from "./useAction";
7
+ export { useSuspend } from "./useSuspend";
8
+ export { useCatchError } from "./useCatchError";
7
9
  export { useRef } from "./useRef";
8
10
  export { useView } from "./useView";
9
11
  export { useEffect } from "./useEffect";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EACL,UAAU,EACV,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,cAAc,EACd,SAAS,GACV,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EACL,UAAU,EACV,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,cAAc,EACd,SAAS,GACV,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -3,7 +3,9 @@ export { useCleanup, useMountEffect, RaskStatefulComponent, RaskStatelessCompone
3
3
  export { createContext, useContext, useInjectContext } from "./createContext";
4
4
  export { useState, assignState } from "./useState";
5
5
  export { useAsync } from "./useAsync";
6
- export { ErrorBoundary } from "./error";
6
+ export { useAction } from "./useAction";
7
+ export { useSuspend } from "./useSuspend";
8
+ export { useCatchError } from "./useCatchError";
7
9
  export { useRef } from "./useRef";
8
10
  export { useView } from "./useView";
9
11
  export { useEffect } from "./useEffect";
@@ -77,7 +77,7 @@ export declare class Observer {
77
77
  private subsHead;
78
78
  private subsTail;
79
79
  private readonly onNotify;
80
- constructor(onNotify: () => void);
80
+ constructor(onNotify: () => void, shouldQueue?: boolean);
81
81
  /** Called from Signal.notify() */
82
82
  _notify(): void;
83
83
  /** Subscribe this observer to a signal */
@@ -1 +1 @@
1
- {"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../src/observation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AASH,wBAAgB,kBAAkB,yBAEjC;AAED;;;GAGG;AACH,cAAM,YAAY;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IAGnB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IACzC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IAGzC,cAAc,EAAE,YAAY,GAAG,IAAI,CAAQ;IAC3C,cAAc,EAAE,YAAY,GAAG,IAAI,CAAQ;IAG3C,MAAM,UAAQ;IAGd,cAAc,EAAE,MAAM,CAAC;gBAEX,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM;CAK9D;AAED;;GAEG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,IAAI,CAA6B;IAGzC,OAAO,CAAC,KAAK,CAAK;IAElB,8DAA8D;IAC9D,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,YAAY;IAe5C,wDAAwD;IACxD,YAAY,CAAC,GAAG,EAAE,YAAY;IAe9B;;OAEG;IACH,MAAM;CAgBP;AAED;;;GAGG;AACH,qBAAa,QAAQ;IACnB,UAAU,UAAS;IAGnB,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,QAAQ,CAA6B;IAG7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAa;gBAE1B,QAAQ,EAAE,MAAM,IAAI;IAUhC,kCAAkC;IAClC,OAAO;IAMP,0CAA0C;IAC1C,eAAe,CAAC,MAAM,EAAE,MAAM;IAe9B,oDAAoD;IACpD,OAAO,CAAC,YAAY;IAiBpB,kCAAkC;IAClC,OAAO;IAUP,sCAAsC;IACtC,OAAO;CAKR"}
1
+ {"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../src/observation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AASH,wBAAgB,kBAAkB,yBAEjC;AAED;;;GAGG;AACH,cAAM,YAAY;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IAGnB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IACzC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IAGzC,cAAc,EAAE,YAAY,GAAG,IAAI,CAAQ;IAC3C,cAAc,EAAE,YAAY,GAAG,IAAI,CAAQ;IAG3C,MAAM,UAAQ;IAGd,cAAc,EAAE,MAAM,CAAC;gBAEX,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM;CAK9D;AAED;;GAEG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,IAAI,CAA6B;IAGzC,OAAO,CAAC,KAAK,CAAK;IAElB,8DAA8D;IAC9D,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,YAAY;IAe5C,wDAAwD;IACxD,YAAY,CAAC,GAAG,EAAE,YAAY;IAe9B;;OAEG;IACH,MAAM;CAgBP;AAED;;;GAGG;AACH,qBAAa,QAAQ;IACnB,UAAU,UAAS;IAGnB,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,QAAQ,CAA6B;IAG7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAa;gBAE1B,QAAQ,EAAE,MAAM,IAAI,EAAE,WAAW,UAAO;IAcpD,kCAAkC;IAClC,OAAO;IAMP,0CAA0C;IAC1C,eAAe,CAAC,MAAM,EAAE,MAAM;IAe9B,oDAAoD;IACpD,OAAO,CAAC,YAAY;IAiBpB,kCAAkC;IAClC,OAAO;IAUP,sCAAsC;IACtC,OAAO;CAKR"}
@@ -133,14 +133,19 @@ export class Observer {
133
133
  subsTail = null;
134
134
  // Only ONE notify callback closure per observer
135
135
  onNotify;
136
- constructor(onNotify) {
136
+ constructor(onNotify, shouldQueue = true) {
137
137
  const onNotifyQueued = onNotify;
138
138
  onNotifyQueued.__queued = false;
139
- this.onNotify = () => {
140
- if (onNotifyQueued.__queued)
141
- return;
142
- queue(onNotify);
143
- };
139
+ if (shouldQueue) {
140
+ this.onNotify = () => {
141
+ if (onNotifyQueued.__queued)
142
+ return;
143
+ queue(onNotify);
144
+ };
145
+ }
146
+ else {
147
+ this.onNotify = onNotify;
148
+ }
144
149
  }
145
150
  /** Called from Signal.notify() */
146
151
  _notify() {
@@ -0,0 +1,18 @@
1
+ export type QueuedAction<P> = {
2
+ params: P;
3
+ error: Error | null;
4
+ retry(): void;
5
+ cancel(): void;
6
+ };
7
+ export type ActionState<P> = {
8
+ isPending: boolean;
9
+ queue: QueuedAction<P>[];
10
+ };
11
+ export type Action<P = null> = [
12
+ ActionState<P>,
13
+ [
14
+ P
15
+ ] extends [null] ? () => void : (params: P) => void
16
+ ];
17
+ export declare function useAction<P = null>(fn: [P] extends [null] ? () => Promise<void> : (params: P) => Promise<void>): Action<P>;
18
+ //# sourceMappingURL=useAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAction.d.ts","sourceRoot":"","sources":["../src/useAction.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAC5B,MAAM,EAAE,CAAC,CAAC;IACV,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,MAAM,CAAC,CAAC,GAAG,IAAI,IAAI;IAC7B,WAAW,CAAC,CAAC,CAAC;IACd;QAAC,CAAC;KAAC,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI;CACtD,CAAC;AAEF,wBAAgB,SAAS,CAAC,CAAC,GAAG,IAAI,EAChC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAC1E,MAAM,CAAC,CAAC,CAAC,CAkDX"}
@@ -0,0 +1,43 @@
1
+ import { useState } from "./useState";
2
+ export function useAction(fn) {
3
+ const state = useState({
4
+ isPending: false,
5
+ queue: [],
6
+ });
7
+ const processQueue = () => {
8
+ const next = state.queue[0];
9
+ if (!next) {
10
+ state.isPending = false;
11
+ return;
12
+ }
13
+ state.isPending = true;
14
+ fn(next.params)
15
+ .then(() => {
16
+ state.queue.shift();
17
+ processQueue();
18
+ })
19
+ .catch((error) => {
20
+ next.error = error;
21
+ });
22
+ };
23
+ const run = (params) => {
24
+ params = (params || null);
25
+ let actionProxy;
26
+ const index = state.queue.push({
27
+ params,
28
+ error: null,
29
+ retry() {
30
+ processQueue();
31
+ },
32
+ cancel() {
33
+ state.queue.splice(state.queue.indexOf(actionProxy), 1);
34
+ processQueue();
35
+ },
36
+ }) - 1;
37
+ actionProxy = state.queue[index];
38
+ if (index === 0) {
39
+ processQueue();
40
+ }
41
+ };
42
+ return [state, run];
43
+ }
@@ -1,27 +1,33 @@
1
- export type AsyncState<P, T, I = null> = {
2
- isPending: false;
3
- params: null;
4
- value: I;
5
- error: null;
1
+ export type AsyncState<T> = {
2
+ error: Error;
3
+ isLoading: true;
4
+ isRefreshing: false;
5
+ value: null;
6
+ } | {
7
+ error: Error;
8
+ isLoading: false;
9
+ isRefreshing: true;
10
+ value: T;
6
11
  } | {
7
- isPending: true;
8
- value: T | I;
9
- params: P;
10
12
  error: null;
13
+ isLoading: true;
14
+ isRefreshing: false;
15
+ value: null;
11
16
  } | {
12
- isPending: false;
13
- params: null;
14
- value: T;
15
17
  error: null;
18
+ isLoading: false;
19
+ isRefreshing: true;
20
+ value: T;
16
21
  } | {
17
- isPending: false;
18
- params: null;
19
- value: T | I;
20
- error: string;
22
+ error: null;
23
+ isLoading: false;
24
+ isRefreshing: false;
25
+ value: T;
21
26
  };
22
- export type Async<A, B = never, I = null> = [B] extends [never] ? [AsyncState<null, A, I>, () => void] : [AsyncState<A, B, I>, (params: A) => void];
23
- export declare function useAsync<T>(initialValue: T, fn: (params: undefined, signal: AbortSignal) => Promise<T>): Async<T, never, T>;
24
- export declare function useAsync<P, T>(initialValue: T, fn: (params: P, signal: AbortSignal) => Promise<T>): Async<P, T, T>;
25
- export declare function useAsync<T>(fn: (params: undefined, signal: AbortSignal) => Promise<T>): Async<T>;
26
- export declare function useAsync<P, T>(fn: (params: P, signal: AbortSignal) => Promise<T>): Async<P, T>;
27
+ export type Async<T extends NonNullable<any>> = [
28
+ AsyncState<T>,
29
+ () => Promise<void>
30
+ ];
31
+ export declare function isAsync(value: unknown): boolean;
32
+ export declare function useAsync<T extends NonNullable<any>>(fn: (signal?: AbortSignal) => Promise<T>): Async<T>;
27
33
  //# sourceMappingURL=useAsync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAsync.d.ts","sourceRoot":"","sources":["../src/useAsync.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,IACjC;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC;IACb,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,MAAM,EAAE,CAAC,CAAC;IACV,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC;IACb,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC;IACb,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAC3D,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,GACpC,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC;AAE/C,wBAAgB,QAAQ,CAAC,CAAC,EACxB,YAAY,EAAE,CAAC,EACf,EAAE,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GACzD,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AACtB,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAC3B,YAAY,EAAE,CAAC,EACf,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GACjD,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AAClB,wBAAgB,QAAQ,CAAC,CAAC,EACxB,EAAE,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GACzD,KAAK,CAAC,CAAC,CAAC,CAAC;AACZ,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAC3B,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GACjD,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"useAsync.d.ts","sourceRoot":"","sources":["../src/useAsync.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,UAAU,CAAC,CAAC,IACpB;IACE,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,EAAE,KAAK,CAAC;IACpB,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,KAAK,CAAC;IACjB,YAAY,EAAE,IAAI,CAAC;IACnB,KAAK,EAAE,CAAC,CAAC;CACV,GACD;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,EAAE,KAAK,CAAC;IACpB,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,SAAS,EAAE,KAAK,CAAC;IACjB,YAAY,EAAE,IAAI,CAAC;IACnB,KAAK,EAAE,CAAC,CAAC;CACV,GACD;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,SAAS,EAAE,KAAK,CAAC;IACjB,YAAY,EAAE,KAAK,CAAC;IACpB,KAAK,EAAE,CAAC,CAAC;CACV,CAAC;AAEN,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,WAAW,CAAC,GAAG,CAAC,IAAI;IAC9C,UAAU,CAAC,CAAC,CAAC;IACb,MAAM,OAAO,CAAC,IAAI,CAAC;CACpB,CAAC;AAEF,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,WAWrC;AAED,wBAAgB,QAAQ,CAAC,CAAC,SAAS,WAAW,CAAC,GAAG,CAAC,EACjD,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GA0InC,KAAK,CAAC,CAAC,CAAC,CACd"}
package/dist/useAsync.js CHANGED
@@ -1,72 +1,131 @@
1
+ import { syncBatch } from "./batch";
1
2
  import { useCleanup, getCurrentComponent } from "./component";
3
+ import { Observer } from "./observation";
2
4
  import { assignState, useState } from "./useState";
3
- export function useAsync(...args) {
5
+ export function isAsync(value) {
6
+ if (value === null || typeof value !== "object") {
7
+ return false;
8
+ }
9
+ return Boolean("isLoading" in value &&
10
+ "isRefreshing" in value &&
11
+ "error" in value &&
12
+ "value" in value);
13
+ }
14
+ export function useAsync(fn) {
4
15
  const currentComponent = getCurrentComponent();
5
16
  if (!currentComponent || currentComponent.isRendering) {
6
- throw new Error("Only use useTask in component setup");
17
+ throw new Error("Only use useAsync in component setup");
7
18
  }
8
- const value = args.length === 2 ? args[0] : null;
9
- const fn = args.length === 2 ? args[1] : args[0];
10
19
  const state = useState({
11
- isPending: false,
12
- value,
20
+ isLoading: true,
21
+ isRefreshing: false,
22
+ value: null,
13
23
  error: null,
14
- params: null,
15
24
  });
25
+ const refreshResolvers = [];
16
26
  let currentAbortController;
17
- const fetch = (params) => {
27
+ const refresh = () => {
18
28
  currentAbortController?.abort();
19
29
  const abortController = (currentAbortController = new AbortController());
20
- const promise = fn(params, abortController.signal);
30
+ const stopObserving = observer.observe();
31
+ const promise = fn(abortController.signal);
32
+ stopObserving();
21
33
  promise
22
34
  .then((result) => {
23
35
  if (abortController.signal.aborted) {
24
36
  return;
25
37
  }
26
- assignState(state, {
27
- isPending: false,
28
- value: result,
29
- error: null,
30
- params: null,
38
+ syncBatch(() => {
39
+ assignState(state, {
40
+ isLoading: false,
41
+ isRefreshing: false,
42
+ value: result,
43
+ error: null,
44
+ });
31
45
  });
46
+ refreshResolvers.forEach((resolver) => resolver.resolve());
47
+ refreshResolvers.length = 0;
32
48
  })
33
49
  .catch((error) => {
34
50
  if (abortController.signal.aborted) {
35
51
  return;
36
52
  }
37
- assignState(state, {
38
- isPending: false,
39
- value: state.value,
40
- error: String(error),
41
- params: null,
53
+ syncBatch(() => {
54
+ assignState(state, {
55
+ isLoading: state.isLoading,
56
+ isRefreshing: state.isRefreshing,
57
+ value: state.value,
58
+ error,
59
+ });
42
60
  });
61
+ refreshResolvers.forEach((resolver) => resolver.reject(error));
62
+ refreshResolvers.length = 0;
43
63
  });
44
64
  return promise;
45
65
  };
46
- useCleanup(() => currentAbortController?.abort());
66
+ const observer = new Observer(() => {
67
+ syncBatch(() => {
68
+ if (state.isLoading) {
69
+ refresh();
70
+ }
71
+ else if (state.error && state.value === null) {
72
+ assignState(state, {
73
+ isLoading: true,
74
+ isRefreshing: false,
75
+ value: state.value,
76
+ error: null,
77
+ });
78
+ refresh();
79
+ }
80
+ else {
81
+ assignState(state, {
82
+ isLoading: false,
83
+ isRefreshing: true,
84
+ value: state.value,
85
+ error: null,
86
+ });
87
+ refresh();
88
+ }
89
+ });
90
+ });
91
+ useCleanup(() => {
92
+ currentAbortController?.abort();
93
+ observer.dispose();
94
+ });
95
+ refresh();
47
96
  return [
48
- {
49
- get isPending() {
50
- return state.isPending;
51
- },
52
- get value() {
53
- return state.value;
54
- },
55
- get error() {
56
- return state.error;
57
- },
58
- get params() {
59
- return state.params;
60
- },
61
- },
62
- (params) => {
63
- fetch(params);
64
- assignState(state, {
65
- isPending: true,
66
- value: state.value,
67
- error: null,
68
- params: (params || null),
97
+ state,
98
+ async () => {
99
+ if (state.isLoading && !state.error) {
100
+ return;
101
+ }
102
+ syncBatch(() => {
103
+ if (state.error && state.value === null) {
104
+ assignState(state, {
105
+ isLoading: true,
106
+ isRefreshing: false,
107
+ value: state.value,
108
+ error: null,
109
+ });
110
+ }
111
+ else {
112
+ assignState(state, {
113
+ isLoading: false,
114
+ isRefreshing: true,
115
+ value: state.value,
116
+ error: null,
117
+ });
118
+ }
119
+ });
120
+ let resolve;
121
+ let reject;
122
+ const promise = new Promise((res, rej) => {
123
+ resolve = res;
124
+ reject = rej;
69
125
  });
126
+ refreshResolvers.push({ resolve, reject });
127
+ refresh();
128
+ return promise;
70
129
  },
71
130
  ];
72
131
  }
@@ -0,0 +1,5 @@
1
+ export declare const CatchErrorContext: import("./createContext").Context<(error: unknown) => void>;
2
+ export declare function useCatchError(): {
3
+ error: unknown;
4
+ };
5
+ //# sourceMappingURL=useCatchError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCatchError.d.ts","sourceRoot":"","sources":["../src/useCatchError.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB,4CAAyB,OAAO,KAAK,IAAI,CAAG,CAAC;AAE3E,wBAAgB,aAAa;WAQK,OAAO;EAOxC"}
@@ -0,0 +1,16 @@
1
+ import { useState } from "./useState";
2
+ import { createContext, useInjectContext } from "./createContext";
3
+ import { getCurrentComponent } from "./component";
4
+ export const CatchErrorContext = createContext();
5
+ export function useCatchError() {
6
+ const currentComponent = getCurrentComponent();
7
+ if (!currentComponent || currentComponent.isRendering) {
8
+ throw new Error("Only use the useCatchError hook in setup");
9
+ }
10
+ const inject = useInjectContext(CatchErrorContext);
11
+ const state = useState({
12
+ error: null,
13
+ });
14
+ inject((error) => (state.error = error));
15
+ return state;
16
+ }
@@ -23,7 +23,7 @@ export function useDerived(computed) {
23
23
  value,
24
24
  });
25
25
  }
26
- });
26
+ }, false);
27
27
  useCleanup(() => computedObserver.dispose());
28
28
  Object.defineProperty(proxy, prop, {
29
29
  enumerable: true,
@@ -0,0 +1,40 @@
1
+ import { AsyncState } from "./useAsync";
2
+ type SuspendState<T extends Record<string, () => any>> = {
3
+ error: Error;
4
+ isLoading: true;
5
+ isRefreshing: false;
6
+ values: {
7
+ [K in keyof T]: ReturnType<T[K]> extends AsyncState<any> ? ReturnType<T[K]>["value"] : ReturnType<T[K]>;
8
+ };
9
+ } | {
10
+ error: Error;
11
+ isLoading: false;
12
+ isRefreshing: true;
13
+ values: {
14
+ [K in keyof T]: ReturnType<T[K]> extends AsyncState<any> ? NonNullable<ReturnType<T[K]>["value"]> : ReturnType<T[K]>;
15
+ };
16
+ } | {
17
+ error: null;
18
+ isLoading: true;
19
+ isRefreshing: false;
20
+ values: {
21
+ [K in keyof T]: ReturnType<T[K]> extends AsyncState<any> ? ReturnType<T[K]>["value"] : ReturnType<T[K]>;
22
+ };
23
+ } | {
24
+ error: null;
25
+ isLoading: false;
26
+ isRefreshing: true;
27
+ values: {
28
+ [K in keyof T]: ReturnType<T[K]> extends AsyncState<any> ? NonNullable<ReturnType<T[K]>["value"]> : ReturnType<T[K]>;
29
+ };
30
+ } | {
31
+ error: null;
32
+ isLoading: false;
33
+ isRefreshing: false;
34
+ values: {
35
+ [K in keyof T]: ReturnType<T[K]> extends AsyncState<any> ? NonNullable<ReturnType<T[K]>["value"]> : ReturnType<T[K]>;
36
+ };
37
+ };
38
+ export declare function useSuspend<T extends Record<string, any>>(asyncs: T): SuspendState<T>;
39
+ export {};
40
+ //# sourceMappingURL=useSuspend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSuspend.d.ts","sourceRoot":"","sources":["../src/useSuspend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAW,MAAM,YAAY,CAAC;AAIjD,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,IACjD;IACE,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,EAAE,KAAK,CAAC;IACpB,MAAM,EAAE;SACL,CAAC,IAAI,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,GAAG,CAAC,GACpD,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GACzB,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACrB,CAAC;CACH,GACD;IACE,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,KAAK,CAAC;IACjB,YAAY,EAAE,IAAI,CAAC;IACnB,MAAM,EAAE;SACL,CAAC,IAAI,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,GAAG,CAAC,GACpD,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GACtC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACrB,CAAC;CACH,GACD;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,EAAE,KAAK,CAAC;IACpB,MAAM,EAAE;SACL,CAAC,IAAI,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,GAAG,CAAC,GACpD,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GACzB,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACrB,CAAC;CACH,GACD;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,SAAS,EAAE,KAAK,CAAC;IACjB,YAAY,EAAE,IAAI,CAAC;IACnB,MAAM,EAAE;SACL,CAAC,IAAI,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,GAAG,CAAC,GACpD,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GACtC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACrB,CAAC;CACH,GACD;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,SAAS,EAAE,KAAK,CAAC;IACjB,YAAY,EAAE,KAAK,CAAC;IACpB,MAAM,EAAE;SACL,CAAC,IAAI,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,GAAG,CAAC,GACpD,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GACtC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACrB,CAAC;CACH,CAAC;AAEN,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,mBAsDlE"}
@@ -0,0 +1,48 @@
1
+ import { isAsync } from "./useAsync";
2
+ import { useEffect } from "./useEffect";
3
+ import { assignState, useState } from "./useState";
4
+ export function useSuspend(asyncs) {
5
+ const state = useState({
6
+ isLoading: true,
7
+ isRefreshing: false,
8
+ error: null,
9
+ values: Object.keys(asyncs).reduce((aggr, key) => {
10
+ let value = asyncs[key]();
11
+ if (isAsync(value)) {
12
+ value = value.value;
13
+ }
14
+ aggr[key] = value;
15
+ return aggr;
16
+ }, {}),
17
+ });
18
+ useEffect(() => {
19
+ let isLoading = false;
20
+ let isRefreshing = false;
21
+ let error;
22
+ const values = {};
23
+ for (const key in asyncs) {
24
+ let value = asyncs[key]();
25
+ if (!isAsync(value)) {
26
+ values[key] = value;
27
+ continue;
28
+ }
29
+ values[key] = value.value;
30
+ if (value.isLoading) {
31
+ isLoading = true;
32
+ }
33
+ else if (value.isRefreshing) {
34
+ isRefreshing = true;
35
+ }
36
+ if (value.error) {
37
+ error = value.error;
38
+ }
39
+ }
40
+ state.isLoading = isLoading;
41
+ state.isRefreshing = isLoading ? false : isRefreshing;
42
+ state.error = error || null;
43
+ if (!state.isLoading && !state.isRefreshing && !error) {
44
+ assignState(state.values, values);
45
+ }
46
+ });
47
+ return state;
48
+ }
@@ -0,0 +1,18 @@
1
+ import { AsyncState } from "./useAsync";
2
+ type SuspendAsyncState<T extends Record<string, AsyncState<any>>> = {
3
+ values: {
4
+ [K in keyof T]: T[K]["value"];
5
+ };
6
+ } & ({
7
+ isPending: true;
8
+ error: null;
9
+ } | {
10
+ isPending: false;
11
+ error: string;
12
+ } | {
13
+ isPending: false;
14
+ error: null;
15
+ });
16
+ export declare function useSuspendAsync<T extends Record<string, AsyncState<any>>>(asyncs: T): SuspendAsyncState<T>;
17
+ export {};
18
+ //# sourceMappingURL=useSuspendAsync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSuspendAsync.d.ts","sourceRoot":"","sources":["../src/useSuspendAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIxC,KAAK,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI;IAClE,MAAM,EAAE;SACL,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;KAC9B,CAAC;CACH,GAAG,CACA;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,IAAI,CAAC;CACb,CACJ,CAAC;AAEF,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,EACvE,MAAM,EAAE,CAAC,wBAyCV"}
@@ -0,0 +1,37 @@
1
+ import { useEffect } from "./useEffect";
2
+ import { useState } from "./useState";
3
+ export function useSuspendAsync(asyncs) {
4
+ const state = useState({
5
+ isPending: false,
6
+ error: null,
7
+ values: Object.keys(asyncs).reduce((aggr, key) => ({ ...aggr, [key]: asyncs[key].value }), {}),
8
+ });
9
+ useEffect(() => {
10
+ let isPending = false;
11
+ let error = "";
12
+ for (const key in asyncs) {
13
+ if (asyncs[key].error) {
14
+ error += asyncs[key].error + "\n";
15
+ continue;
16
+ }
17
+ if (asyncs[key].isPending) {
18
+ isPending = true;
19
+ continue;
20
+ }
21
+ }
22
+ if (error) {
23
+ state.isPending = false;
24
+ state.error = error;
25
+ return;
26
+ }
27
+ if (isPending) {
28
+ state.isPending = false;
29
+ state.error = null;
30
+ return;
31
+ }
32
+ for (const key in asyncs) {
33
+ state.values[key] = asyncs[key].value;
34
+ }
35
+ });
36
+ return state;
37
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rask-ui",
3
- "version": "0.18.2",
3
+ "version": "0.19.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",