diff --git a/packages/page-staking/src/Bags/Bag.tsx b/packages/page-staking/src/Bags/Bag.tsx index a58a57ab13..842ff601f8 100644 --- a/packages/page-staking/src/Bags/Bag.tsx +++ b/packages/page-staking/src/Bags/Bag.tsx @@ -2,13 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import type { u64 } from '@polkadot/types'; +import type { AccountId32 } from '@polkadot/types/interfaces'; import type { PalletBagsListListBag } from '@polkadot/types/lookup'; import type { StashNode } from './types'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { AddressMini, Spinner } from '@polkadot/react-components'; -import { useIncrement } from '@polkadot/react-hooks'; import { FormatBalance } from '@polkadot/react-query'; import { formatNumber } from '@polkadot/util'; @@ -22,27 +22,24 @@ interface Props { stashNodes?: StashNode[]; } -export default function Bag ({ id, info, stashNodes }: Props): React.ReactElement { +function Bag ({ id, info, stashNodes }: Props): React.ReactElement { + const [[headId, trigger], setHeadId] = useState<[AccountId32 | null, number]>([null, 0]); const [isLoading, setLoading] = useState(true); - const [trigger, doRefresh] = useIncrement(1); - const headId = useMemo( - () => stashNodes && stashNodes.length - ? info.head.unwrapOr(null) - : null, - [info, stashNodes] - ); - const list = useBagEntries(headId, trigger); + const [isCompleted, list] = useBagEntries(headId, trigger); const bonded = useBonded(list); + useEffect((): void => { + info && stashNodes && + setHeadId(([, trigger]) => [info.head.unwrapOr(null), ++trigger]); + }, [info, stashNodes]); + useEffect((): void => { setLoading( stashNodes && stashNodes.length - ? list - ? !bonded - : true + ? !isCompleted || !bonded : false ); - }, [bonded, list, stashNodes]); + }, [bonded, isCompleted, stashNodes]); return ( @@ -52,7 +49,6 @@ export default function Bag ({ id, info, stashNodes }: Props): React.ReactElemen {stashNodes?.map(({ stashId }) => ( ); } + +export default React.memo(Bag); diff --git a/packages/page-staking/src/Bags/Stash.tsx b/packages/page-staking/src/Bags/Stash.tsx index 2e72731f32..0dce3f6cc1 100644 --- a/packages/page-staking/src/Bags/Stash.tsx +++ b/packages/page-staking/src/Bags/Stash.tsx @@ -13,7 +13,6 @@ import { useTranslation } from '../translate'; interface Props { className?: string; - doRefresh: () => void; isLoading: boolean; list?: ListNode[]; stashId: string; @@ -26,7 +25,7 @@ function findEntry (stashId: string, list: ListNode[] = []): [ListNode | null, b return [entry, !!other, entry && other ? entry.index - other.index : 0]; } -function Stash ({ className, doRefresh, isLoading, list, stashId }: Props): React.ReactElement { +function Stash ({ className, isLoading, list, stashId }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const [stashInfo, canJump, jumpCount] = useMemo( @@ -45,7 +44,6 @@ function Stash ({ className, doRefresh, isLoading, list, stashId }: Props): Reac icon='caret-up' isDisabled={!canJump || isLoading} label={t('Move up {{jumpCount}}', { replace: { jumpCount } })} - onSuccess={doRefresh} params={[stashInfo?.jump]} tx={api.tx.bagsList.putInFrontOf} /> @@ -53,7 +51,7 @@ function Stash ({ className, doRefresh, isLoading, list, stashId }: Props): Reac ); } -export default styled(Stash)` +export default React.memo(styled(Stash)` .ui--AddressMini { vertical-align: middle; } @@ -62,4 +60,4 @@ export default styled(Stash)` cursor: default; opacity: 0; } -`; +`); diff --git a/packages/page-staking/src/Bags/Summary.tsx b/packages/page-staking/src/Bags/Summary.tsx index a6d0949574..c1e9a6ede0 100644 --- a/packages/page-staking/src/Bags/Summary.tsx +++ b/packages/page-staking/src/Bags/Summary.tsx @@ -15,7 +15,7 @@ interface Props { ids?: u64[]; } -export default function Summary ({ className = '', ids }: Props): React.ReactElement { +function Summary ({ className = '', ids }: Props): React.ReactElement { const { t } = useTranslation(); return ( @@ -29,3 +29,5 @@ export default function Summary ({ className = '', ids }: Props): React.ReactEle ); } + +export default React.memo(Summary); diff --git a/packages/page-staking/src/Bags/index.tsx b/packages/page-staking/src/Bags/index.tsx index c66ce03d88..f05832b065 100644 --- a/packages/page-staking/src/Bags/index.tsx +++ b/packages/page-staking/src/Bags/index.tsx @@ -18,7 +18,7 @@ interface Props { ownStashes?: StakerState[]; } -export default function Bags ({ ownStashes }: Props): React.ReactElement { +function Bags ({ ownStashes }: Props): React.ReactElement { const { t } = useTranslation(); const ids = useBagsIds(); const list = useBagsList(ids); @@ -39,7 +39,7 @@ export default function Bags ({ ownStashes }: Props): React.ReactElement ]); const sorted = useMemo( - () => list + () => list && nodes ? [...list].sort((a, b) => nodes[a[0]] ? nodes[b[0]] @@ -57,7 +57,7 @@ export default function Bags ({ ownStashes }: Props): React.ReactElement <> ('No available bags')} + empty={nodes && list && list.length === 0 && t('No available bags')} emptySpinner={t('Retrieving all available bags, this will take some time')} header={headerRef.current} > @@ -73,3 +73,5 @@ export default function Bags ({ ownStashes }: Props): React.ReactElement ); } + +export default React.memo(Bags); diff --git a/packages/page-staking/src/Bags/useBagEntries.tsx b/packages/page-staking/src/Bags/useBagEntries.tsx index 0be4aa72dd..74edc267ba 100644 --- a/packages/page-staking/src/Bags/useBagEntries.tsx +++ b/packages/page-staking/src/Bags/useBagEntries.tsx @@ -17,7 +17,7 @@ interface Result { const EMPTY: [AccountId32 | null, Result] = [null, { isCompleted: false, list: [] }]; const EMPTY_LIST: AccountId32[] = []; -function useBagEntriesImpl (headId: AccountId32 | null, trigger: number): AccountId32[] { +function useBagEntriesImpl (headId: AccountId32 | null, trigger: number): [boolean, AccountId32[]] { const { api } = useApi(); const [[currId, { isCompleted, list }], setCurrent] = useState<[AccountId32 | null, Result]>(EMPTY); const node = useCall>(!!currId && api.query.bagsList.listNodes, [currId]); @@ -45,9 +45,12 @@ function useBagEntriesImpl (headId: AccountId32 | null, trigger: number): Accoun } }, [node]); - return isCompleted - ? list - : EMPTY_LIST; + return [ + isCompleted, + isCompleted + ? list + : EMPTY_LIST + ]; } export default createNamedHook('useBagEntries', useBagEntriesImpl); diff --git a/packages/page-staking/src/Bags/useBagsNodes.tsx b/packages/page-staking/src/Bags/useBagsNodes.tsx index 09a5eac040..7f514e9c42 100644 --- a/packages/page-staking/src/Bags/useBagsNodes.tsx +++ b/packages/page-staking/src/Bags/useBagsNodes.tsx @@ -5,12 +5,13 @@ import type { Option } from '@polkadot/types'; import type { PalletBagsListListNode } from '@polkadot/types/lookup'; import type { StashNode } from './types'; +import { useEffect, useState } from 'react'; + import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks'; type Result = Record; const multiOptions = { - defaultValue: {} as Result, transform: (opts: Option[]): Result => opts .filter((o) => o.isSome) @@ -28,10 +29,28 @@ const multiOptions = { }, {}) }; +function merge (prev: Result, curr: Result): Result { + return Object + .entries(curr) + .reduce((all: Result, [id, nodes]): Result => { + all[id] = prev[id] && JSON.stringify(nodes) === JSON.stringify(prev[id]) + ? prev[id] + : nodes; + + return all; + }, {}); +} + function useBagsNodesImpl (stashIds: string[]): Result { const { api } = useApi(); + const [result, setResult] = useState({}); + const query = useCall(stashIds && stashIds.length !== 0 && api.query.bagsList.listNodes.multi, [stashIds], multiOptions); - return useCall(stashIds && stashIds.length !== 0 && api.query.bagsList.listNodes.multi, [stashIds], multiOptions) as Result; + useEffect((): void => { + query && setResult((prev) => merge(prev, query)); + }, [query]); + + return result; } export default createNamedHook('useBagsNodes', useBagsNodesImpl);