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:
Jaco 2023-01-21 11:28:42 +02:00 committed by GitHub
parent 48e881d6d1
commit 7b685cec66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 41 deletions

View File

@ -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}>

View File

@ -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';

View File

@ -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]);

View 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;
}