From 6aefdd917827106455008a91e1f5bdc4125ca994 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sat, 1 Aug 2020 15:29:52 +0200 Subject: [PATCH] useLoadingDelay for large lists (#3310) * useLoadingDelay for large lists * Accounts with isLoading * sortedAddresses afecting check --- packages/page-accounts/src/Accounts/index.tsx | 9 +++++---- packages/page-accounts/src/Contacts/index.tsx | 9 +++++---- .../page-staking/src/Overview/CurrentList.tsx | 13 ++++++++----- .../src/InputAddressMulti/index.tsx | 8 ++------ packages/react-components/src/Table/Body.tsx | 2 +- packages/react-components/src/Table/index.tsx | 2 +- packages/react-hooks/src/index.ts | 1 + packages/react-hooks/src/useLoadingDelay.ts | 19 +++++++++++++++++++ 8 files changed, 42 insertions(+), 21 deletions(-) create mode 100644 packages/react-hooks/src/useLoadingDelay.ts diff --git a/packages/page-accounts/src/Accounts/index.tsx b/packages/page-accounts/src/Accounts/index.tsx index 945a83f9c0..790913aa7b 100644 --- a/packages/page-accounts/src/Accounts/index.tsx +++ b/packages/page-accounts/src/Accounts/index.tsx @@ -11,7 +11,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import keyring from '@polkadot/ui-keyring'; import { getLedger, isLedger } from '@polkadot/react-api'; -import { useApi, useAccounts, useCall, useFavorites, useIpfs, useToggle } from '@polkadot/react-hooks'; +import { useApi, useAccounts, useCall, useFavorites, useIpfs, useLoadingDelay, useToggle } from '@polkadot/react-hooks'; import { FormatBalance } from '@polkadot/react-query'; import { Button, Input, Table } from '@polkadot/react-components'; import { BN_ZERO } from '@polkadot/util'; @@ -70,10 +70,11 @@ function Overview ({ className = '', onStatusChange }: Props): React.ReactElemen const [favorites, toggleFavorite] = useFavorites(STORE_FAVS); const [{ balanceTotal }, setBalances] = useState({ accounts: {} }); const [filterOn, setFilter] = useState(''); - const [sortedAccountsWithDelegation, setSortedAccountsWithDelegation] = useState([]); + const [sortedAccountsWithDelegation, setSortedAccountsWithDelegation] = useState(); const [{ sortedAccounts, sortedAddresses }, setSorted] = useState({ sortedAccounts: [], sortedAddresses: [] }); const delegations = useCall(api.query.democracy?.votingOf?.multi, [sortedAddresses]); const proxies = useCall<[[AccountId, ProxyType][], BN][]>(api.query.proxy?.proxies.multi, [sortedAddresses]); + const isLoading = useLoadingDelay(); useEffect((): void => { const sortedAccounts = sortAccounts(allAccounts, favorites); @@ -234,12 +235,12 @@ function Overview ({ className = '', onStatusChange }: Props): React.ReactElemen /> ("You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.")} + empty={!isLoading && sortedAccountsWithDelegation && t("You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.")} filter={filter} footer={footer} header={header} > - {sortedAccountsWithDelegation.map(({ account, delegation, isFavorite }, index): React.ReactNode => ( + {isLoading ? undefined : sortedAccountsWithDelegation?.map(({ account, delegation, isFavorite }, index): React.ReactNode => ( ([]); + const [sortedAddresses, setSortedAddresses] = useState(); const [filterOn, setFilter] = useState(''); + const isLoading = useLoadingDelay(); useEffect((): void => { setSortedAddresses( @@ -76,11 +77,11 @@ function Overview ({ className = '', onStatusChange }: Props): React.ReactElemen /> )}
('no addresses saved yet, add any existing address')} + empty={!isLoading && sortedAddresses && t('no addresses saved yet, add any existing address')} filter={filter} header={header} > - {sortedAddresses.map(({ address, isFavorite }): React.ReactNode => ( + {isLoading ? undefined : sortedAddresses?.map(({ address, isFavorite }): React.ReactNode => (
| null>(); const [withIdentity, setWithIdentity] = useState(false); + // we have a very large list, so we use a loading delay + const isLoading = useLoadingDelay(); + useEffect((): void => { stakingOverview && setFiltered( getFiltered(stakingOverview, favorites, next) @@ -156,7 +159,7 @@ function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOvervi return isIntentions ? (
('No waiting validators found')} + empty={!isLoading && waiting && t('No waiting validators found')} filter={ - {_renderRows(elected, false).concat(_renderRows(waiting, false))} + {isLoading ? undefined : _renderRows(elected, false).concat(_renderRows(waiting, false))}
) : ( ('No active validators found')} + empty={!isLoading && validators && t('No active validators found')} filter={ - {_renderRows(validators, true)} + {isLoading ? undefined : _renderRows(validators, true)}
); } diff --git a/packages/react-components/src/InputAddressMulti/index.tsx b/packages/react-components/src/InputAddressMulti/index.tsx index fc673a7800..623399c710 100644 --- a/packages/react-components/src/InputAddressMulti/index.tsx +++ b/packages/react-components/src/InputAddressMulti/index.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import styled from 'styled-components'; -import { useDebounce } from '@polkadot/react-hooks'; +import { useDebounce, useLoadingDelay } from '@polkadot/react-hooks'; import { useTranslation } from '../translate'; import Input from '../Input'; @@ -28,11 +28,7 @@ function InputAddressMulti ({ available, availableLabel, className = '', default const [_filter, setFilter] = useState(''); const [selected, setSelected] = useState([]); const filter = useDebounce(_filter); - const [isLoading, setIsLoading] = useState(true); - - useEffect((): void => { - setTimeout(() => setIsLoading(false), 250); - }); + const isLoading = useLoadingDelay(); useEffect((): void => { defaultValue && setSelected(defaultValue); diff --git a/packages/react-components/src/Table/Body.tsx b/packages/react-components/src/Table/Body.tsx index 39b655a4d2..c862c80988 100644 --- a/packages/react-components/src/Table/Body.tsx +++ b/packages/react-components/src/Table/Body.tsx @@ -11,7 +11,7 @@ import Spinner from '../Spinner'; interface Props { children?: React.ReactNode; className?: string; - empty?: React.ReactNode; + empty?: React.ReactNode | false; emptySpinner?: React.ReactNode; } diff --git a/packages/react-components/src/Table/index.tsx b/packages/react-components/src/Table/index.tsx index f0fe819fbe..c2cc929c33 100644 --- a/packages/react-components/src/Table/index.tsx +++ b/packages/react-components/src/Table/index.tsx @@ -12,7 +12,7 @@ import Head from './Head'; interface TableProps { children: React.ReactNode; className?: string; - empty?: React.ReactNode; + empty?: React.ReactNode | false; emptySpinner?: React.ReactNode; filter?: React.ReactNode; footer?: React.ReactNode; diff --git a/packages/react-hooks/src/index.ts b/packages/react-hooks/src/index.ts index 39e1f14515..171a889739 100644 --- a/packages/react-hooks/src/index.ts +++ b/packages/react-hooks/src/index.ts @@ -18,6 +18,7 @@ export { default as useFavorites } from './useFavorites'; export { default as useIncrement } from './useIncrement'; export { default as useIpfs } from './useIpfs'; export { default as useIsMountedRef } from './useIsMountedRef'; +export { default as useLoadingDelay } from './useLoadingDelay'; export { default as useMembers } from './useMembers'; export { default as useModal } from './useModal'; export { default as useNonEmptyString } from './useNonEmptyString'; diff --git a/packages/react-hooks/src/useLoadingDelay.ts b/packages/react-hooks/src/useLoadingDelay.ts new file mode 100644 index 0000000000..c8589a3d6a --- /dev/null +++ b/packages/react-hooks/src/useLoadingDelay.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/react-hooks authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { useEffect, useState } from 'react'; +import useIsMountedRef from './useIsMountedRef'; + +export default function useLoadingDelay (delay = 100): boolean { + const mountedRef = useIsMountedRef(); + const [isLoading, setIsLoading] = useState(true); + + useEffect((): void => { + setTimeout((): void => { + mountedRef.current && setIsLoading(false); + }, delay); + }); + + return isLoading; +}