From 57fd733985405e79015024ae913c1fb9325ab7c7 Mon Sep 17 00:00:00 2001 From: Jaco Date: Tue, 31 Jan 2023 19:56:15 +0200 Subject: [PATCH] Adjust list layouts (#8905) * Adjust list layouts * Generic useCache{Map, Value} * usePoints with useCacheValue --- .../src/Validators/Active/Entry.tsx | 105 ++++++++++++++++ .../src/Validators/Active/index.tsx | 116 +++++------------- .../src/Validators/ListActive.tsx | 57 --------- .../src/Validators/ListWaiting.tsx | 55 --------- .../src/Validators/Waiting/Entry.tsx | 57 +++++++++ .../src/Validators/Waiting/index.tsx | 74 ++++++----- .../page-staking2/src/Validators/index.tsx | 8 +- .../Validators/{Active => }/useExposure.ts | 32 +---- .../Validators/{Active => }/useHeartbeat.ts | 33 +---- .../page-staking2/src/Validators/usePoints.ts | 22 +--- packages/page-staking2/src/types.ts | 5 + packages/page-staking2/src/useCacheMap.ts | 34 +++++ packages/page-staking2/src/useCacheValue.ts | 45 +++++++ .../page-staking2/src/useElectedValidators.ts | 28 +---- .../page-staking2/src/useValidatorsActive.ts | 23 +--- .../page-staking2/src/useValidatorsAll.ts | 13 +- .../page-staking2/src/useValidatorsWaiting.ts | 23 +--- 17 files changed, 347 insertions(+), 383 deletions(-) create mode 100644 packages/page-staking2/src/Validators/Active/Entry.tsx delete mode 100644 packages/page-staking2/src/Validators/ListActive.tsx delete mode 100644 packages/page-staking2/src/Validators/ListWaiting.tsx create mode 100644 packages/page-staking2/src/Validators/Waiting/Entry.tsx rename packages/page-staking2/src/Validators/{Active => }/useExposure.ts (65%) rename packages/page-staking2/src/Validators/{Active => }/useHeartbeat.ts (61%) create mode 100644 packages/page-staking2/src/useCacheMap.ts create mode 100644 packages/page-staking2/src/useCacheValue.ts diff --git a/packages/page-staking2/src/Validators/Active/Entry.tsx b/packages/page-staking2/src/Validators/Active/Entry.tsx new file mode 100644 index 0000000000..3423144f74 --- /dev/null +++ b/packages/page-staking2/src/Validators/Active/Entry.tsx @@ -0,0 +1,105 @@ +// Copyright 2017-2023 @polkadot/app-staking authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { SessionInfo, Validator } from '../../types'; + +import React, { useMemo, useRef } from 'react'; + +import { Table, Tag } from '@polkadot/react-components'; +import { useToggle } from '@polkadot/react-hooks'; +import { FormatBalance } from '@polkadot/react-query'; +import { formatNumber } from '@polkadot/util'; + +import useExposure from '../useExposure'; +import useHeartbeat from '../useHeartbeat'; +import Bottom from './Row/Bottom'; +import Middle from './Row/Middle'; +import Top from './Row/Top'; + +interface Props { + className?: string; + isRelay: boolean; + points?: number; + sessionInfo: SessionInfo; + toggleFavorite: (stashId: string) => void; + validator: Validator; +} + +interface PropsExpanded { + className?: string; + validator: Validator; +} + +function EntryExpanded ({ className = '' }: PropsExpanded): React.ReactElement { + return ; +} + +function Entry ({ className = '', isRelay, points, sessionInfo, toggleFavorite, validator }: Props): React.ReactElement { + const [isExpanded, toggleExpanded] = useToggle(); + const pointsRef = useRef<{ counter: number, points: number }>({ counter: 0, points: 0 }); + const exposure = useExposure(validator, sessionInfo); + const heartbeat = useHeartbeat(validator, sessionInfo); + + const pointsAnimClass = useMemo( + (): string => { + if (!points || pointsRef.current.points === points) { + return ''; + } + + pointsRef.current.points = points; + pointsRef.current.counter = (pointsRef.current.counter + 1) % 25; + + return `greyAnim-${pointsRef.current.counter}`; + }, + [points, pointsRef] + ); + + return ( + <> + + {points && ( + + )} + + + + ( + + , {exposure.waiting.others.length}) + + ) + } + value={exposure?.clipped?.total} + withLoading + /> + + + + + + ); +} + +export default React.memo(Entry); diff --git a/packages/page-staking2/src/Validators/Active/index.tsx b/packages/page-staking2/src/Validators/Active/index.tsx index c51dd02b1f..575a05dd12 100644 --- a/packages/page-staking2/src/Validators/Active/index.tsx +++ b/packages/page-staking2/src/Validators/Active/index.tsx @@ -2,103 +2,55 @@ // SPDX-License-Identifier: Apache-2.0 import type { SessionInfo, Validator } from '../../types'; +import type { UsePoints } from '../types'; -import React, { useMemo, useRef } from 'react'; +import React, { useRef } from 'react'; -import { Table, Tag } from '@polkadot/react-components'; -import { useToggle } from '@polkadot/react-hooks'; -import { FormatBalance } from '@polkadot/react-query'; -import { formatNumber } from '@polkadot/util'; +import Legend from '@polkadot/app-staking/Legend'; +import { Table } from '@polkadot/react-components'; +import { useNextTick } from '@polkadot/react-hooks'; -import Bottom from './Row/Bottom'; -import Middle from './Row/Middle'; -import Top from './Row/Top'; -import useExposure from './useExposure'; -import useHeartbeat from './useHeartbeat'; +import { useTranslation } from '../../translate'; +import Entry from './Entry'; interface Props { className?: string; isRelay: boolean; - points?: number; + points?: UsePoints; sessionInfo: SessionInfo; toggleFavorite: (stashId: string) => void; - validator: Validator; + validatorsActive?: Validator[]; } -interface PropsExpanded { - className?: string; - validator: Validator; -} +function Active ({ className = '', isRelay, points, sessionInfo, toggleFavorite, validatorsActive }: Props): React.ReactElement { + const { t } = useTranslation(); + const isNextTick = useNextTick(); -function ActiveExpanded ({ className = '' }: PropsExpanded): React.ReactElement { - return ; -} - -function Active ({ className = '', isRelay, points, sessionInfo, toggleFavorite, validator }: Props): React.ReactElement { - const [isExpanded, toggleExpanded] = useToggle(); - const pointsRef = useRef<{ counter: number, points: number }>({ counter: 0, points: 0 }); - const exposure = useExposure(validator, sessionInfo); - const heartbeat = useHeartbeat(validator, sessionInfo); - - const pointsAnimClass = useMemo( - (): string => { - if (!points || pointsRef.current.points === points) { - return ''; - } - - pointsRef.current.points = points; - pointsRef.current.counter = (pointsRef.current.counter + 1) % 25; - - return `greyAnim-${pointsRef.current.counter}`; - }, - [points, pointsRef] - ); + const header = useRef<[string?, string?, number?][]>([ + // favorite, badges, details, expand + [t('validators'), 'start', 4] + ]); return ( - <> - - {points && ( - - )} - - - - ( - - , {exposure.waiting.others.length}) - - ) - } - value={exposure?.clipped?.total} - withLoading + ('No session validators found')} + emptySpinner={t('Retrieving session validators')} + header={header.current} + isSplit + legend={} + > + {isNextTick && validatorsActive?.map((v) => ( + - - - - - + ))} +
); } diff --git a/packages/page-staking2/src/Validators/ListActive.tsx b/packages/page-staking2/src/Validators/ListActive.tsx deleted file mode 100644 index f10d6a5165..0000000000 --- a/packages/page-staking2/src/Validators/ListActive.tsx +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2017-2023 @polkadot/app-staking authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -import type { SessionInfo, Validator } from '../types'; -import type { UsePoints } from './types'; - -import React, { useRef } from 'react'; - -import Legend from '@polkadot/app-staking/Legend'; -import { Table } from '@polkadot/react-components'; -import { useNextTick } from '@polkadot/react-hooks'; - -import { useTranslation } from '../translate'; -import Active from './Active'; - -interface Props { - className?: string; - isRelay: boolean; - points?: UsePoints; - sessionInfo: SessionInfo; - toggleFavorite: (stashId: string) => void; - validatorsActive?: Validator[]; -} - -function ListActive ({ className = '', isRelay, points, sessionInfo, toggleFavorite, validatorsActive }: Props): React.ReactElement { - const { t } = useTranslation(); - const isNextTick = useNextTick(); - - const header = useRef<[string?, string?, number?][]>([ - // favorite, badges, details, expand - [t('validators'), 'start', 4] - ]); - - return ( - ('No session validators found')} - emptySpinner={t('Retrieving session validators')} - header={header.current} - isSplit - legend={} - > - {isNextTick && validatorsActive?.map((v) => ( - - ))} -
- ); -} - -export default React.memo(ListActive); diff --git a/packages/page-staking2/src/Validators/ListWaiting.tsx b/packages/page-staking2/src/Validators/ListWaiting.tsx deleted file mode 100644 index 92bc74310d..0000000000 --- a/packages/page-staking2/src/Validators/ListWaiting.tsx +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2017-2023 @polkadot/app-staking authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -import type { SessionInfo, Validator } from '../types'; - -import React, { useRef } from 'react'; - -import Legend from '@polkadot/app-staking/Legend'; -import { Table } from '@polkadot/react-components'; -import { useNextTick } from '@polkadot/react-hooks'; - -import { useTranslation } from '../translate'; -import useValidatorsWaiting from '../useValidatorsWaiting'; -import Waiting from './Waiting'; - -interface Props { - className?: string; - favorites: string[]; - isRelay: boolean; - sessionInfo: SessionInfo; - toggleFavorite: (stashId: string) => void; - validatorsActive?: Validator[]; -} - -function ListActive ({ className = '', favorites, isRelay, sessionInfo, toggleFavorite, validatorsActive }: Props): React.ReactElement { - const { t } = useTranslation(); - const isNextTick = useNextTick(); - const validatorsWaiting = useValidatorsWaiting(favorites, sessionInfo, validatorsActive); - - const header = useRef<[string?, string?, number?][]>([ - // favorite, badges, details, expand - [t('waiting'), 'start', 4] - ]); - - return ( - ('No waiting validators found')} - emptySpinner={t('Retrieving waiting validators')} - header={header.current} - isSplit - legend={} - > - {isNextTick && validatorsWaiting?.map((v) => ( - - ))} -
- ); -} - -export default React.memo(ListActive); diff --git a/packages/page-staking2/src/Validators/Waiting/Entry.tsx b/packages/page-staking2/src/Validators/Waiting/Entry.tsx new file mode 100644 index 0000000000..e5b36f1274 --- /dev/null +++ b/packages/page-staking2/src/Validators/Waiting/Entry.tsx @@ -0,0 +1,57 @@ +// Copyright 2017-2023 @polkadot/app-staking authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { Validator } from '../../types'; + +import React from 'react'; + +import { useToggle } from '@polkadot/react-hooks'; + +import Bottom from '../Active/Row/Bottom'; +import Middle from '../Active/Row/Middle'; +import Top from '../Active/Row/Top'; + +interface Props { + className?: string; + toggleFavorite: (stashId: string) => void; + validator: Validator; +} + +interface PropsExpanded { + className?: string; + validator: Validator; +} + +function EntryExpanded ({ className = '' }: PropsExpanded): React.ReactElement { + return ; +} + +function Entry ({ className = '', toggleFavorite, validator }: Props): React.ReactElement { + const [isExpanded, toggleExpanded] = useToggle(); + + return ( + <> + + + + + + + + + ); +} + +export default React.memo(Entry); diff --git a/packages/page-staking2/src/Validators/Waiting/index.tsx b/packages/page-staking2/src/Validators/Waiting/index.tsx index ccbad006eb..1e87f561fc 100644 --- a/packages/page-staking2/src/Validators/Waiting/index.tsx +++ b/packages/page-staking2/src/Validators/Waiting/index.tsx @@ -1,56 +1,54 @@ // Copyright 2017-2023 @polkadot/app-staking authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { Validator } from '../../types'; +import type { SessionInfo, Validator } from '../../types'; -import React from 'react'; +import React, { useRef } from 'react'; -import { useToggle } from '@polkadot/react-hooks'; +import Legend from '@polkadot/app-staking/Legend'; +import { Table } from '@polkadot/react-components'; +import { useNextTick } from '@polkadot/react-hooks'; -import Bottom from '../Active/Row/Bottom'; -import Middle from '../Active/Row/Middle'; -import Top from '../Active/Row/Top'; +import { useTranslation } from '../../translate'; +import useValidatorsWaiting from '../../useValidatorsWaiting'; +import Entry from './Entry'; interface Props { className?: string; + favorites: string[]; + isRelay: boolean; + sessionInfo: SessionInfo; toggleFavorite: (stashId: string) => void; - validator: Validator; + validatorsActive?: Validator[]; } -interface PropsExpanded { - className?: string; - validator: Validator; -} +function Waiting ({ className = '', favorites, isRelay, sessionInfo, toggleFavorite, validatorsActive }: Props): React.ReactElement { + const { t } = useTranslation(); + const isNextTick = useNextTick(); + const validatorsWaiting = useValidatorsWaiting(favorites, sessionInfo, validatorsActive); -function WaitingExpanded ({ className = '' }: PropsExpanded): React.ReactElement { - return ; -} - -function Waiting ({ className = '', toggleFavorite, validator }: Props): React.ReactElement { - const [isExpanded, toggleExpanded] = useToggle(); + const header = useRef<[string?, string?, number?][]>([ + // favorite, badges, details, expand + [t('waiting'), 'start', 4] + ]); return ( - <> - - - - - - - - + ('No waiting validators found')} + emptySpinner={t('Retrieving waiting validators')} + header={header.current} + isSplit + legend={} + > + {isNextTick && validatorsWaiting?.map((v) => ( + + ))} +
); } diff --git a/packages/page-staking2/src/Validators/index.tsx b/packages/page-staking2/src/Validators/index.tsx index 808faa06e0..dcc2599c42 100644 --- a/packages/page-staking2/src/Validators/index.tsx +++ b/packages/page-staking2/src/Validators/index.tsx @@ -10,9 +10,9 @@ import { Button, ToggleGroup } from '@polkadot/react-components'; import { useTranslation } from '../translate'; import useValidatorsActive from '../useValidatorsActive'; -import ListActive from './ListActive'; -import ListWaiting from './ListWaiting'; +import Active from './Active'; import usePoints from './usePoints'; +import Waiting from './Waiting'; interface Props { className?: string; @@ -44,7 +44,7 @@ function Validators ({ className = '', favorites, isRelay, sessionInfo, toggleFa {intentIndex === 0 ? ( - ) : ( - ; +import { useCacheMap } from '../useCacheMap'; const OPT_EXPOSURE = { transform: ({ others, own, total }: PalletStakingExposure): UseExposureExposure => ({ @@ -48,8 +44,6 @@ function getResult (exposure: UseExposureExposure, clipped: UseExposureExposure) return { clipped, exposure, waiting }; } -const cache: Cache = {}; - function useExposureImpl ({ stashId }: Validator, { activeEra }: SessionInfo): UseExposure | undefined { const { api } = useApi(); @@ -66,21 +60,7 @@ function useExposureImpl ({ stashId }: Validator, { activeEra }: SessionInfo): U [clipExposure, fullExposure] ); - // clear the cached result on era changes - useEffect((): void => { - if (activeEra && (!cache[stashId] || !activeEra.eq(cache[stashId].activeEra))) { - cache[stashId] = { activeEra }; - } - }, [activeEra, stashId]); - - // update the cached result on exposure changes - useEffect((): void => { - if (result && activeEra && cache[stashId] && activeEra.eq(cache[stashId].activeEra)) { - cache[stashId] = objectSpread({ activeEra }, result); - } - }, [activeEra, result, stashId]); - - return result || cache[stashId]; + return useCacheMap('useExposure', stashId, activeEra, result); } export default createNamedHook('useExposure', useExposureImpl); diff --git a/packages/page-staking2/src/Validators/Active/useHeartbeat.ts b/packages/page-staking2/src/Validators/useHeartbeat.ts similarity index 61% rename from packages/page-staking2/src/Validators/Active/useHeartbeat.ts rename to packages/page-staking2/src/Validators/useHeartbeat.ts index 8fbdbd83a0..06efa4e83b 100644 --- a/packages/page-staking2/src/Validators/Active/useHeartbeat.ts +++ b/packages/page-staking2/src/Validators/useHeartbeat.ts @@ -3,20 +3,15 @@ import type { Option, u32 } from '@polkadot/types'; import type { Codec } from '@polkadot/types/types'; -import type { BN } from '@polkadot/util'; -import type { SessionInfo, Validator } from '../../types'; -import type { UseHeartbeat } from '../types'; +import type { SessionInfo, Validator } from '../types'; +import type { UseHeartbeat } from './types'; -import { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks'; -import { isBoolean, isNumber, objectSpread } from '@polkadot/util'; +import { isBoolean, isNumber } from '@polkadot/util'; -interface CacheEntry extends UseHeartbeat { - currentSession: BN; -} - -type Cache = Record; +import { useCacheMap } from '../useCacheMap'; const EMPTY: UseHeartbeat = {}; @@ -31,8 +26,6 @@ const OPT_BEATS = { receivedHeartbeats.isSome }; -const cache: Cache = {}; - function useHeartbeatImpl ({ stashId, stashIndex }: Validator, { currentSession }: SessionInfo): UseHeartbeat { const { api } = useApi(); @@ -57,21 +50,7 @@ function useHeartbeatImpl ({ stashId, stashIndex }: Validator, { currentSession [authoredBlocks, receivedHeartbeats] ); - // when the session changes, empty the cache - useEffect((): void => { - if (currentSession && (!cache[stashId] || !currentSession.eq(cache[stashId].currentSession))) { - cache[stashId] = { currentSession }; - } - }, [currentSession, stashId]); - - // set the new heartbeat info based on retrieved - useEffect((): void => { - if (result && currentSession && currentSession.eq(cache[stashId].currentSession)) { - cache[stashId] = objectSpread({ currentSession }, result); - } - }, [currentSession, result, stashId]); - - return result || cache[stashId] || EMPTY; + return useCacheMap('useHeartbeat', stashId, currentSession, result) || EMPTY; } export default createNamedHook('useHeartbeat', useHeartbeatImpl); diff --git a/packages/page-staking2/src/Validators/usePoints.ts b/packages/page-staking2/src/Validators/usePoints.ts index 52ae7ceccd..34d48bcd1d 100644 --- a/packages/page-staking2/src/Validators/usePoints.ts +++ b/packages/page-staking2/src/Validators/usePoints.ts @@ -2,19 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import type { PalletStakingEraRewardPoints } from '@polkadot/types/lookup'; -import type { BN } from '@polkadot/util'; import type { SessionInfo } from '../types'; import type { UsePoints } from './types'; -import { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks'; import { BN_ZERO } from '@polkadot/util'; -interface Cache { - activeEra: BN; - points?: UsePoints; -} +import { useCacheValue } from '../useCacheValue'; const OPT_POINTS = { transform: ({ individual }: PalletStakingEraRewardPoints): UsePoints => @@ -27,8 +23,6 @@ const OPT_POINTS = { }, {}) }; -let cache: Cache | undefined; - function usePointsImpl ({ activeEra }: SessionInfo): UsePoints | undefined { const { api } = useApi(); @@ -39,17 +33,7 @@ function usePointsImpl ({ activeEra }: SessionInfo): UsePoints | undefined { const points = useCall(queryParams && api.query.staking.erasRewardPoints, queryParams, OPT_POINTS); - useEffect((): void => { - if (activeEra && points) { - cache = { activeEra, points }; - } - }, [activeEra, points]); - - return points || ( - cache && - activeEra?.eq(cache.activeEra) && - cache.points - ) || undefined; + return useCacheValue('usePoints', activeEra, points); } export default createNamedHook('usePoints', usePointsImpl); diff --git a/packages/page-staking2/src/types.ts b/packages/page-staking2/src/types.ts index 645529e8b4..2f39d32be5 100644 --- a/packages/page-staking2/src/types.ts +++ b/packages/page-staking2/src/types.ts @@ -3,6 +3,11 @@ import type { BN } from '@polkadot/util'; +export interface CacheValue { + check: BN; + value?: T; +} + export interface SessionInfo { activeEra?: BN | null; currentEra?: BN | null; diff --git a/packages/page-staking2/src/useCacheMap.ts b/packages/page-staking2/src/useCacheMap.ts new file mode 100644 index 0000000000..1629543283 --- /dev/null +++ b/packages/page-staking2/src/useCacheMap.ts @@ -0,0 +1,34 @@ +// Copyright 2017-2023 @polkadot/app-staking authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { BN } from '@polkadot/util'; +import type { CacheValue } from './types'; + +import { useEffect } from 'react'; + +import { getResultValue } from './useCacheValue'; + +type CacheSection = `use${'Exposure' | 'Heartbeat'}`; + +const cache: Record>> = { + useExposure: {}, + useHeartbeat: {} +}; + +export function useCacheMap (section: CacheSection, id: string, check?: BN | null, value?: T): T | undefined { + // ensure that the specific id has an entry + useEffect((): void => { + if (check && (!cache[section][id] || !cache[section][id].check.eq(check))) { + cache[section][id] = { check }; + } + }, [check, id, section]); + + // update the cached result on value changes + useEffect((): void => { + if (value && check && cache[section][id] && cache[section][id].check.eq(check)) { + cache[section][id] = { check, value }; + } + }, [check, id, section, value]); + + return getResultValue(cache[section][id] as CacheValue, check, value); +} diff --git a/packages/page-staking2/src/useCacheValue.ts b/packages/page-staking2/src/useCacheValue.ts new file mode 100644 index 0000000000..9ee8954f3b --- /dev/null +++ b/packages/page-staking2/src/useCacheValue.ts @@ -0,0 +1,45 @@ +// Copyright 2017-2023 @polkadot/app-staking authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { CacheValue } from './types'; + +import { useEffect } from 'react'; + +import { BN } from '@polkadot/util'; + +type CacheSection = `use${'ElectedValidators' | 'Points' | 'ValidatorsActive' | 'ValidatorsAll' | 'ValidatorsWaiting'}`; + +const cache: Record> = { + useElectedValidators: { check: new BN(-1) }, + usePoints: { check: new BN(-1) }, + useValidatorsActive: { check: new BN(-1) }, + useValidatorsAll: { check: new BN(-1) }, + useValidatorsWaiting: { check: new BN(-1) } +}; + +export function getResultValue (cached?: CacheValue, check?: BN | null, value?: T): T | undefined { + return value || ( + check && + cached && + cached.check.eq(check) && + cached.value + ) || undefined; +} + +export function useCacheValue (section: CacheSection, check?: BN | null, value?: T): T | undefined { + // ensure that the specific id has an entry + useEffect((): void => { + if (check && !cache[section].check.eq(check)) { + cache[section] = { check }; + } + }, [check, section]); + + // update the cached result on value changes + useEffect((): void => { + if (value && check && cache[section].check.eq(check)) { + cache[section] = { check, value }; + } + }, [check, section, value]); + + return getResultValue(cache[section] as CacheValue, check, value); +} diff --git a/packages/page-staking2/src/useElectedValidators.ts b/packages/page-staking2/src/useElectedValidators.ts index 7295ec0499..3fcbb15a15 100644 --- a/packages/page-staking2/src/useElectedValidators.ts +++ b/packages/page-staking2/src/useElectedValidators.ts @@ -3,17 +3,13 @@ import type { StorageKey, u32 } from '@polkadot/types'; import type { AccountId32 } from '@polkadot/types/interfaces'; -import type { BN } from '@polkadot/util'; import type { SessionInfo } from './types'; -import { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import { createNamedHook, useApi, useMapKeys } from '@polkadot/react-hooks'; -interface Cache { - currentEra: BN; - elected: string[]; -} +import { useCacheValue } from './useCacheValue'; const OPT_ELECTED = { transform: (keys: StorageKey<[u32, AccountId32]>[]): string[] => @@ -22,33 +18,17 @@ const OPT_ELECTED = { ) }; -// since this would only change once per era for the full runtime, -// we just store it globally to not have expensive map-key lookups -let cache: Cache | undefined; - function useElectedValidatorsImpl ({ currentEra }: SessionInfo): string[] | undefined { const { api } = useApi(); const electedParams = useMemo( - () => currentEra && (!cache || !cache.currentEra.eq(currentEra)) - ? [currentEra] - : null, + () => currentEra && [currentEra], [currentEra] ); const elected = useMapKeys(electedParams && api.query.staking.erasStakers, electedParams, OPT_ELECTED); - useEffect((): void => { - if (currentEra && elected) { - cache = { currentEra, elected }; - } - }, [currentEra, elected]); - - return elected || ( - cache && - currentEra?.eq(cache.currentEra) && - cache.elected - ) || undefined; + return useCacheValue('useElectedValidators', currentEra, elected); } export default createNamedHook('useElectedValidators', useElectedValidatorsImpl); diff --git a/packages/page-staking2/src/useValidatorsActive.ts b/packages/page-staking2/src/useValidatorsActive.ts index f99739c7ef..d4d095f9b1 100644 --- a/packages/page-staking2/src/useValidatorsActive.ts +++ b/packages/page-staking2/src/useValidatorsActive.ts @@ -2,20 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import type { AccountId32 } from '@polkadot/types/interfaces'; -import type { BN } from '@polkadot/util'; import type { SessionInfo, Validator } from './types'; -import { useEffect } from 'react'; - import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks'; +import { useCacheValue } from './useCacheValue'; import useTaggedValidators from './useTaggedValidators'; -interface Cache { - activeEra: BN; - tagged: Validator[]; -} - const OPT_VALIDATORS = { transform: (validators: AccountId32[]): Validator[] => validators.map((a, stashIndex) => { @@ -32,24 +25,12 @@ const OPT_VALIDATORS = { }) }; -let cache: Cache | undefined; - function useValidatorsActiveImpl (favorites: string[], sessionInfo: SessionInfo): Validator[] | undefined { const { api } = useApi(); const sessionValidators = useCall(api.query.session.validators, undefined, OPT_VALIDATORS); const tagged = useTaggedValidators(favorites, sessionInfo, sessionValidators); - useEffect((): void => { - if (tagged && sessionInfo.activeEra) { - cache = { activeEra: sessionInfo.activeEra, tagged }; - } - }, [sessionInfo, tagged]); - - return tagged || ( - cache && - sessionInfo.activeEra?.eq(cache.activeEra) && - cache.tagged - ) || undefined; + return useCacheValue('useValidatorsActive', sessionInfo.activeEra, tagged); } export default createNamedHook('useValidatorsActive', useValidatorsActiveImpl); diff --git a/packages/page-staking2/src/useValidatorsAll.ts b/packages/page-staking2/src/useValidatorsAll.ts index a09aca3a8e..45348ce700 100644 --- a/packages/page-staking2/src/useValidatorsAll.ts +++ b/packages/page-staking2/src/useValidatorsAll.ts @@ -6,10 +6,11 @@ import type { StorageKey } from '@polkadot/types'; import type { AccountId32, EventRecord } from '@polkadot/types/interfaces'; import type { SessionInfo, Validator } from './types'; -import { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import { createNamedHook, useApi, useEventChanges, useMapKeys } from '@polkadot/react-hooks'; +import { useCacheValue } from './useCacheValue'; import useTaggedValidators from './useTaggedValidators'; const EMPTY_PARAMS: unknown[] = []; @@ -49,8 +50,6 @@ function mapValidators (validators?: AccountId32[]): Validator[] | undefined { }); } -let cache: Validator[] | undefined; - function useValidatorsAllImpl (favorites: string[], sessionInfo: SessionInfo): Validator[] | undefined { const { api } = useApi(); const startValue = useMapKeys(api.query.staking.validators, EMPTY_PARAMS, OPT_VALIDATORS); @@ -66,13 +65,7 @@ function useValidatorsAllImpl (favorites: string[], sessionInfo: SessionInfo): V const tagged = useTaggedValidators(favorites, sessionInfo, validatorsIndexed); - useEffect((): void => { - if (tagged) { - cache = tagged; - } - }, [tagged, validatorsIndexed]); - - return tagged || cache; + return useCacheValue('useValidatorsAll', sessionInfo.currentEra, tagged); } export default createNamedHook('useValidatorsAll', useValidatorsAllImpl); diff --git a/packages/page-staking2/src/useValidatorsWaiting.ts b/packages/page-staking2/src/useValidatorsWaiting.ts index 5532ab4eee..3fee0b9dc2 100644 --- a/packages/page-staking2/src/useValidatorsWaiting.ts +++ b/packages/page-staking2/src/useValidatorsWaiting.ts @@ -1,28 +1,21 @@ // Copyright 2017-2023 @polkadot/app-staking authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { BN } from '@polkadot/util'; import type { SessionInfo, Validator } from './types'; -import { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import { createNamedHook } from '@polkadot/react-hooks'; +import { useCacheValue } from './useCacheValue'; import useValidatorsAll from './useValidatorsAll'; -interface Cache { - activeEra: BN; - tagged: Validator[]; -} - function excludeValidators (from: Validator[], exclude: Validator[]): Validator[] { return from.filter(({ stashId }) => !exclude.some((v) => v.stashId === stashId) ); } -let cache: Cache | undefined; - function useValidatorsWaitingImpl (favorites: string[], sessionInfo: SessionInfo, activeValidators?: Validator[]): Validator[] | undefined { const allValidators = useValidatorsAll(favorites, sessionInfo); @@ -33,17 +26,7 @@ function useValidatorsWaitingImpl (favorites: string[], sessionInfo: SessionInfo [activeValidators, allValidators] ); - useEffect((): void => { - if (tagged && sessionInfo.activeEra) { - cache = { activeEra: sessionInfo.activeEra, tagged }; - } - }, [sessionInfo, tagged]); - - return tagged || ( - cache && - sessionInfo.activeEra?.eq(cache.activeEra) && - cache.tagged - ) || undefined; + return useCacheValue('useValidatorsWaiting', sessionInfo.activeEra, tagged); } export default createNamedHook('useValidatorsWaiting', useValidatorsWaitingImpl);