useLoadingDelay for large lists (#3310)

* useLoadingDelay for large lists

* Accounts with isLoading

* sortedAddresses afecting check
This commit is contained in:
Jaco Greeff 2020-08-01 15:29:52 +02:00 committed by GitHub
parent 3b4752106c
commit 6aefdd9178
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 42 additions and 21 deletions

View File

@ -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<Balances>({ accounts: {} });
const [filterOn, setFilter] = useState<string>('');
const [sortedAccountsWithDelegation, setSortedAccountsWithDelegation] = useState<SortedAccount[]>([]);
const [sortedAccountsWithDelegation, setSortedAccountsWithDelegation] = useState<SortedAccount[] | undefined>();
const [{ sortedAccounts, sortedAddresses }, setSorted] = useState<Sorted>({ sortedAccounts: [], sortedAddresses: [] });
const delegations = useCall<Voting[]>(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
/>
</Button.Group>
<Table
empty={t<string>("You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.")}
empty={!isLoading && sortedAccountsWithDelegation && t<string>("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 => (
<Account
account={account}
delegation={delegation}

View File

@ -7,7 +7,7 @@ import { ComponentProps as Props } from '../types';
import React, { useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { Button, Input, Table } from '@polkadot/react-components';
import { useAddresses, useFavorites, useToggle } from '@polkadot/react-hooks';
import { useAddresses, useFavorites, useLoadingDelay, useToggle } from '@polkadot/react-hooks';
import CreateModal from './modals/Create';
import Address from './Address';
@ -22,8 +22,9 @@ function Overview ({ className = '', onStatusChange }: Props): React.ReactElemen
const { allAddresses } = useAddresses();
const [isCreateOpen, toggleCreate] = useToggle(false);
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS);
const [sortedAddresses, setSortedAddresses] = useState<SortedAddress[]>([]);
const [sortedAddresses, setSortedAddresses] = useState<SortedAddress[] | undefined>();
const [filterOn, setFilter] = useState<string>('');
const isLoading = useLoadingDelay();
useEffect((): void => {
setSortedAddresses(
@ -76,11 +77,11 @@ function Overview ({ className = '', onStatusChange }: Props): React.ReactElemen
/>
)}
<Table
empty={t<string>('no addresses saved yet, add any existing address')}
empty={!isLoading && sortedAddresses && t<string>('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 => (
<Address
address={address}
filter={filterOn}

View File

@ -8,7 +8,7 @@ import { Authors } from '@polkadot/react-query/BlockAuthors';
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { useApi, useCall, useLoadingDelay } from '@polkadot/react-hooks';
import { BlockAuthorsContext } from '@polkadot/react-query';
import { Option, StorageKey } from '@polkadot/types';
@ -101,6 +101,9 @@ function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOvervi
const [nominatedBy, setNominatedBy] = useState<Record<string, [string, number][]> | 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
? (
<Table
empty={waiting && t<string>('No waiting validators found')}
empty={!isLoading && waiting && t<string>('No waiting validators found')}
filter={
<Filtering
nameFilter={nameFilter}
@ -167,12 +170,12 @@ function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOvervi
}
header={headerWaiting}
>
{_renderRows(elected, false).concat(_renderRows(waiting, false))}
{isLoading ? undefined : _renderRows(elected, false).concat(_renderRows(waiting, false))}
</Table>
)
: (
<Table
empty={validators && t<string>('No active validators found')}
empty={!isLoading && validators && t<string>('No active validators found')}
filter={
<Filtering
nameFilter={nameFilter}
@ -183,7 +186,7 @@ function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOvervi
}
header={headerActive}
>
{_renderRows(validators, true)}
{isLoading ? undefined : _renderRows(validators, true)}
</Table>
);
}

View File

@ -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<string>('');
const [selected, setSelected] = useState<string[]>([]);
const filter = useDebounce(_filter);
const [isLoading, setIsLoading] = useState(true);
useEffect((): void => {
setTimeout(() => setIsLoading(false), 250);
});
const isLoading = useLoadingDelay();
useEffect((): void => {
defaultValue && setSelected(defaultValue);

View File

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

View File

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

View File

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

View File

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