Add useTimer hook (& use in ApiStatsCtx) (#8857)
* Add useTimer hook (& use in ApiStatsCtx) * Adjust useBounce (w/ timerId var) * Adjust getStats & mergeStats
This commit is contained in:
parent
48e881d6d1
commit
7b685cec66
@ -5,10 +5,10 @@ import type { ApiPromise } from '@polkadot/api';
|
||||
import type { ProviderStats } from '@polkadot/rpc-provider/types';
|
||||
import type { ApiStats } from './types';
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { useApi } from '../useApi';
|
||||
import { useIsMountedRef } from '../useIsMountedRef';
|
||||
import { useTimer } from '../useTimer';
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
@ -35,10 +35,6 @@ function getStats (...apis: ApiPromise[]): { stats: ProviderStats, when: number
|
||||
}
|
||||
};
|
||||
|
||||
if (apis.length === 0) {
|
||||
return { stats, when: Date.now() };
|
||||
}
|
||||
|
||||
for (let i = 0; i < apis.length; i++) {
|
||||
const s = apis[i].stats;
|
||||
|
||||
@ -61,42 +57,26 @@ function getStats (...apis: ApiPromise[]): { stats: ProviderStats, when: number
|
||||
};
|
||||
}
|
||||
|
||||
function mergeStats (curr: ApiStats, prev: ApiStats[]): ApiStats[] {
|
||||
return prev.length === 0
|
||||
? [curr]
|
||||
: prev.length === MAX_NUM
|
||||
? prev.concat(curr).slice(-MAX_NUM)
|
||||
: prev.concat(curr);
|
||||
}
|
||||
|
||||
export const ApiStatsCtx = React.createContext<ApiStats[]>(EMPTY_STATE);
|
||||
|
||||
export function ApiStatsCtxRoot ({ children }: Props): React.ReactElement<Props> {
|
||||
const { api } = useApi();
|
||||
const [stats, setStats] = useState(EMPTY_STATE);
|
||||
const timerId = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const mountedRef = useIsMountedRef();
|
||||
|
||||
useEffect((): () => void => {
|
||||
function fireTimer (): void {
|
||||
timerId.current = null;
|
||||
const stateFn = useCallback(
|
||||
(prev: ApiStats[]): ApiStats[] =>
|
||||
mergeStats(getStats(api), prev),
|
||||
[api]
|
||||
);
|
||||
|
||||
if (mountedRef.current) {
|
||||
const curr = getStats(api);
|
||||
|
||||
setStats((prev): ApiStats[] => {
|
||||
if (prev.length === 0) {
|
||||
return [curr];
|
||||
}
|
||||
|
||||
return prev.length === MAX_NUM
|
||||
? prev.concat(curr).slice(-MAX_NUM)
|
||||
: prev.concat(curr);
|
||||
});
|
||||
|
||||
timerId.current = setTimeout(fireTimer, INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
fireTimer();
|
||||
|
||||
return (): void => {
|
||||
timerId.current && clearTimeout(timerId.current);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
const stats = useTimer(stateFn, EMPTY_STATE, INTERVAL);
|
||||
|
||||
return (
|
||||
<ApiStatsCtx.Provider value={stats}>
|
||||
|
||||
@ -73,6 +73,7 @@ export { useSudo } from './useSudo';
|
||||
export { useSystemApi } from './useSystemApi';
|
||||
export { useTeleport } from './useTeleport';
|
||||
export { useTheme } from './useTheme';
|
||||
export { useTimer } from './useTimer';
|
||||
export { useToggle } from './useToggle';
|
||||
export { useTreasury } from './useTreasury';
|
||||
export { useTxBatch } from './useTxBatch';
|
||||
|
||||
@ -8,18 +8,18 @@ import { useIsMountedRef } from './useIsMountedRef';
|
||||
const DEFAULT_DELAY = 250;
|
||||
|
||||
// FIXE Due to generics, cannot use createNamedHook
|
||||
export function useDebounce <T> (value: T, delay?: number): T {
|
||||
export function useDebounce <T> (value: T, delay = DEFAULT_DELAY): T {
|
||||
const mountedRef = useIsMountedRef();
|
||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||
|
||||
useEffect((): () => void => {
|
||||
const handler = setTimeout(() => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
mountedRef.current && setDebouncedValue(value);
|
||||
}, delay || DEFAULT_DELAY);
|
||||
}, delay);
|
||||
|
||||
// each time it renders, it clears
|
||||
// each time something changes, we clears
|
||||
return (): void => {
|
||||
clearTimeout(handler);
|
||||
clearTimeout(timeoutId);
|
||||
};
|
||||
}, [delay, value, mountedRef]);
|
||||
|
||||
|
||||
37
packages/react-hooks/src/useTimer.ts
Normal file
37
packages/react-hooks/src/useTimer.ts
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2017-2023 @polkadot/react-components authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useIsMountedRef } from './useIsMountedRef';
|
||||
|
||||
export function useTimer <T> (stateFn: (prev: T) => T, initial: T, interval: number): T {
|
||||
const [state, setState] = useState(initial);
|
||||
const timerId = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const mountedRef = useIsMountedRef();
|
||||
|
||||
useEffect((): () => void => {
|
||||
function fire (): void {
|
||||
timerId.current = null;
|
||||
|
||||
if (mountedRef.current) {
|
||||
try {
|
||||
setState(stateFn);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
timerId.current = setTimeout(fire, interval);
|
||||
}
|
||||
}
|
||||
|
||||
fire();
|
||||
|
||||
return (): void => {
|
||||
timerId.current && clearTimeout(timerId.current);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return state;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user