Adjust error tracking for useCall/useCallMulti (#7368)
This commit is contained in:
parent
16f47b3f30
commit
0ba53ad55e
@ -9,7 +9,7 @@ import type { MountedRef } from './useIsMountedRef';
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { isNull, isUndefined } from '@polkadot/util';
|
||||
import { isFunction, isNull, isUndefined } from '@polkadot/util';
|
||||
|
||||
import { useIsMountedRef } from './useIsMountedRef';
|
||||
|
||||
@ -45,10 +45,12 @@ type CallFn = (...params: unknown[]) => Promise<VoidFn>;
|
||||
export type TrackFn = PromiseResult<AnyFunction> | QueryFn;
|
||||
|
||||
export interface Tracker {
|
||||
error: Error | null;
|
||||
fn: TrackFn | undefined | null | false;
|
||||
isActive: boolean;
|
||||
serialized: string | null;
|
||||
subscriber: TrackFnResult | null;
|
||||
type: 'useCall' | 'useCallMulti';
|
||||
}
|
||||
|
||||
interface TrackerRef {
|
||||
@ -64,6 +66,10 @@ function isMapFn (fn: unknown): fn is QueryMapFn {
|
||||
return !!(fn as QueryTrackFn).meta?.type?.isMap;
|
||||
}
|
||||
|
||||
function isQuery (fn: unknown): fn is QueryableStorageEntry<'promise', []> {
|
||||
return !!fn && !isUndefined((fn as QueryableStorageEntry<'promise', []>).creator);
|
||||
}
|
||||
|
||||
// extract the serialized and mapped params, all ready for use in our call
|
||||
function extractParams <T> (fn: unknown, params: unknown[], { paramMap = transformIdentity }: CallOptions<T> = {}): [string, CallParams | null] {
|
||||
return [
|
||||
@ -74,20 +80,28 @@ function extractParams <T> (fn: unknown, params: unknown[], { paramMap = transfo
|
||||
];
|
||||
}
|
||||
|
||||
export function handleError (error: Error, tracker: TrackerRef, fn?: unknown): void {
|
||||
console.error(
|
||||
tracker.current.error = new Error(`${tracker.current.type}(${
|
||||
isQuery(fn)
|
||||
? `${fn.creator.section}.${fn.creator.method}`
|
||||
: '...'
|
||||
}):: ${error.message}:: ${error.stack || '<unknown>'}`)
|
||||
);
|
||||
}
|
||||
|
||||
// unsubscribe and remove from the tracker
|
||||
export function unsubscribe (tracker: TrackerRef): void {
|
||||
tracker.current.isActive = false;
|
||||
|
||||
if (tracker.current.subscriber) {
|
||||
tracker.current.subscriber.then((u) => (u as VoidFn)()).catch(console.error);
|
||||
tracker.current.subscriber
|
||||
.then((u) => isFunction(u) && (u as VoidFn)())
|
||||
.catch((e) => handleError(e as Error, tracker));
|
||||
tracker.current.subscriber = null;
|
||||
}
|
||||
}
|
||||
|
||||
function isQuery (fn: TrackFn): fn is QueryableStorageEntry<'promise', []> {
|
||||
return !isUndefined((fn as QueryableStorageEntry<'promise', []>).creator);
|
||||
}
|
||||
|
||||
// subscribe, trying to play nice with the browser threads
|
||||
function subscribe <T> (mountedRef: MountedRef, tracker: TrackerRef, fn: TrackFn | undefined, params: CallParams, setValue: (value: any) => void, { transform = transformIdentity, withParams, withParamsTransform }: CallOptions<T> = {}): void {
|
||||
const validParams = params.filter((p) => !isUndefined(p));
|
||||
@ -117,16 +131,10 @@ function subscribe <T> (mountedRef: MountedRef, tracker: TrackerRef, fn: TrackFn
|
||||
: transform(value)
|
||||
);
|
||||
} catch (error) {
|
||||
let extra = '...';
|
||||
|
||||
if (isQuery(fn)) {
|
||||
extra = `${fn.creator.section}.${fn.creator.method}`;
|
||||
}
|
||||
|
||||
throw new Error(`useCall(${extra}):: ${(error as Error).message}:: ${(error as Error).stack || '<unknown>'}`);
|
||||
handleError(error as Error, tracker, fn);
|
||||
}
|
||||
}
|
||||
});
|
||||
}).catch((error) => handleError(error as Error, tracker, fn));
|
||||
} else {
|
||||
tracker.current.subscriber = null;
|
||||
}
|
||||
@ -134,6 +142,16 @@ function subscribe <T> (mountedRef: MountedRef, tracker: TrackerRef, fn: TrackFn
|
||||
}, 0);
|
||||
}
|
||||
|
||||
export function throwOnError (tracker: Tracker): void {
|
||||
if (tracker.error) {
|
||||
const error = tracker.error;
|
||||
|
||||
tracker.error = null;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// tracks a stream, typically an api.* call (derive, rpc, query) that
|
||||
// - returns a promise with an unsubscribe function
|
||||
// - has a callback to set the value
|
||||
@ -141,7 +159,7 @@ function subscribe <T> (mountedRef: MountedRef, tracker: TrackerRef, fn: TrackFn
|
||||
// FIXME This is generic, we cannot really use createNamedHook
|
||||
export function useCall <T> (fn: TrackFn | undefined | null | false, params?: CallParams | null, options?: CallOptions<T>): T | undefined {
|
||||
const mountedRef = useIsMountedRef();
|
||||
const tracker = useRef<Tracker>({ fn: null, isActive: false, serialized: null, subscriber: null });
|
||||
const tracker = useRef<Tracker>({ error: null, fn: null, isActive: false, serialized: null, subscriber: null, type: 'useCall' });
|
||||
const [value, setValue] = useState<T | undefined>((options || {}).defaultValue);
|
||||
|
||||
// initial effect, we need an un-subscription
|
||||
@ -164,5 +182,7 @@ export function useCall <T> (fn: TrackFn | undefined | null | false, params?: Ca
|
||||
}
|
||||
}, [fn, options, mountedRef, params]);
|
||||
|
||||
// throwOnError(tracker.current);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import type { MountedRef } from './useIsMountedRef';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useApi } from './useApi';
|
||||
import { transformIdentity, unsubscribe } from './useCall';
|
||||
import { handleError, transformIdentity, unsubscribe } from './useCall';
|
||||
import { useIsMountedRef } from './useIsMountedRef';
|
||||
|
||||
interface TrackerRef {
|
||||
@ -33,7 +33,6 @@ function subscribe <T> (api: ApiPromise, mountedRef: MountedRef, tracker: Tracke
|
||||
if (filtered.length) {
|
||||
// swap to active mode
|
||||
tracker.current.isActive = true;
|
||||
|
||||
tracker.current.subscriber = api.queryMulti(filtered, (value): void => {
|
||||
// we use the isActive flag here since .subscriber may not be set on immediate callback)
|
||||
if (mountedRef.current && tracker.current.isActive) {
|
||||
@ -51,11 +50,11 @@ function subscribe <T> (api: ApiPromise, mountedRef: MountedRef, tracker: Tracke
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(`useCallMulti(...):: ${(error as Error).message}:: ${(error as Error).stack || '<unknown>'}`);
|
||||
handleError(error as Error, tracker);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}).catch((error) => handleError(error as Error, tracker));
|
||||
} else {
|
||||
tracker.current.subscriber = null;
|
||||
}
|
||||
@ -68,7 +67,7 @@ function subscribe <T> (api: ApiPromise, mountedRef: MountedRef, tracker: Tracke
|
||||
export function useCallMulti <T> (calls?: QueryableStorageMultiArg<'promise'>[] | null | false, options?: CallOptions<T>): T {
|
||||
const { api } = useApi();
|
||||
const mountedRef = useIsMountedRef();
|
||||
const tracker = useRef<Tracker>({ fn: null, isActive: false, serialized: null, subscriber: null });
|
||||
const tracker = useRef<Tracker>({ error: null, fn: null, isActive: false, serialized: null, subscriber: null, type: 'useCallMulti' });
|
||||
const [value, setValue] = useState<T>(() => (options || {}).defaultValue || [] as unknown as T);
|
||||
|
||||
// initial effect, we need an un-subscription
|
||||
@ -90,5 +89,7 @@ export function useCallMulti <T> (calls?: QueryableStorageMultiArg<'promise'>[]
|
||||
}
|
||||
}, [api, calls, options, mountedRef]);
|
||||
|
||||
// throwOnError(tracker.current);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user