Exhaustive hooks (#2463)
* Exhaustive hooks * selectUrl screw-up * Adjust name badge * Cleanup SideBar effects
This commit is contained in:
parent
1f79d75df7
commit
e4c0cfd262
@ -8,10 +8,10 @@
|
||||
"packages/*"
|
||||
],
|
||||
"resolutions": {
|
||||
"@polkadot/api": "^1.9.0-beta.11",
|
||||
"@polkadot/api-contract": "^1.9.0-beta.11",
|
||||
"@polkadot/api": "^1.9.0-beta.12",
|
||||
"@polkadot/api-contract": "^1.9.0-beta.12",
|
||||
"@polkadot/keyring": "^2.7.0-beta.8",
|
||||
"@polkadot/types": "^1.9.0-beta.11",
|
||||
"@polkadot/types": "^1.9.0-beta.12",
|
||||
"@polkadot/util": "^2.7.0-beta.8",
|
||||
"@polkadot/util-crypto": "^2.7.0-beta.8",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
@ -36,7 +36,7 @@
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/register": "^7.9.0",
|
||||
"@babel/runtime": "^7.9.2",
|
||||
"@polkadot/dev": "^0.51.8",
|
||||
"@polkadot/dev": "^0.51.9",
|
||||
"@polkadot/ts": "^0.3.14",
|
||||
"@types/bn.js": "^4.11.6",
|
||||
"@types/chart.js": "^2.9.16",
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.8.7",
|
||||
"@babel/runtime": "^7.9.2",
|
||||
"@polkadot/dev": "^0.51.8",
|
||||
"@polkadot/dev": "^0.51.9",
|
||||
"@polkadot/react-components": "0.40.0-beta.191",
|
||||
"@polkadot/react-signer": "0.40.0-beta.191",
|
||||
"query-string": "^6.11.1"
|
||||
|
||||
@ -29,6 +29,10 @@ interface SidebarState {
|
||||
|
||||
export const PORTAL_ID = 'portals';
|
||||
|
||||
function saveSidebar (sidebar: SidebarState): SidebarState {
|
||||
return store.set('sidebar', sidebar);
|
||||
}
|
||||
|
||||
function Apps ({ className }: Props): React.ReactElement<Props> {
|
||||
const { systemChain, systemName } = useApi();
|
||||
const [sidebar, setSidebar] = useState<SidebarState>({
|
||||
@ -38,24 +42,17 @@ function Apps ({ className }: Props): React.ReactElement<Props> {
|
||||
...store.get('sidebar', {}),
|
||||
isMenu: window.innerWidth < SIDEBAR_MENU_THRESHOLD
|
||||
});
|
||||
const uiHighlight = useMemo((): string | undefined =>
|
||||
getSystemChainColor(systemChain, systemName), [systemChain, systemName]
|
||||
const uiHighlight = useMemo(
|
||||
(): string | undefined => getSystemChainColor(systemChain, systemName),
|
||||
[systemChain, systemName]
|
||||
);
|
||||
const { isCollapsed, isMenu, isMenuOpen } = sidebar;
|
||||
|
||||
const _setSidebar = useCallback(
|
||||
(update: Partial<SidebarState>): void =>
|
||||
setSidebar((sidebar: SidebarState) =>
|
||||
store.set('sidebar', { ...sidebar, ...update })
|
||||
),
|
||||
const _collapse = useCallback(
|
||||
(): void => setSidebar((sidebar: SidebarState) => saveSidebar({ ...sidebar, isCollapsed: !sidebar.isCollapsed })),
|
||||
[]
|
||||
);
|
||||
const _collapse = useCallback(
|
||||
(): void => _setSidebar({ isCollapsed: !isCollapsed }),
|
||||
[isCollapsed]
|
||||
);
|
||||
const _toggleMenu = useCallback(
|
||||
(): void => _setSidebar({ isCollapsed: false, isMenuOpen: true }),
|
||||
(): void => setSidebar((sidebar: SidebarState) => saveSidebar({ ...sidebar, isCollapsed: false, isMenuOpen: true })),
|
||||
[]
|
||||
);
|
||||
const _handleResize = useCallback(
|
||||
@ -64,15 +61,18 @@ function Apps ({ className }: Props): React.ReactElement<Props> {
|
||||
? SideBarTransition.MINIMISED_AND_EXPANDED
|
||||
: SideBarTransition.EXPANDED_AND_MAXIMISED;
|
||||
|
||||
_setSidebar({
|
||||
setSidebar((sidebar: SidebarState) => saveSidebar({
|
||||
...sidebar,
|
||||
isMenu: transition === SideBarTransition.MINIMISED_AND_EXPANDED,
|
||||
isMenuOpen: false,
|
||||
transition
|
||||
});
|
||||
}));
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const { isCollapsed, isMenu, isMenuOpen } = sidebar;
|
||||
|
||||
return (
|
||||
<>
|
||||
<GlobalStyle uiHighlight={defaultColor || uiHighlight} />
|
||||
|
||||
@ -74,7 +74,7 @@ function Status ({ optionsAll, queueAction, stqueue, txqueue }: Props): React.Re
|
||||
const filtered = filterEvents(allAccounts, t, optionsAll, events);
|
||||
|
||||
filtered && queueAction(filtered);
|
||||
}, [allAccounts, events, optionsAll]);
|
||||
}, [allAccounts, events, optionsAll, queueAction, t]);
|
||||
|
||||
return (
|
||||
<StatusDisplay
|
||||
|
||||
@ -74,30 +74,31 @@ function checkVisible (name: string, { api, isApiReady, isApiConnected }: ApiPro
|
||||
}
|
||||
|
||||
function Item ({ route, isCollapsed, onClick }: Props): React.ReactElement<Props> | null {
|
||||
const { Modal, useCounter = DUMMY_COUNTER, display, i18n, icon, name } = route;
|
||||
const { t } = useTranslation();
|
||||
const { allAccounts, hasAccounts } = useAccounts();
|
||||
const apiProps = useApi();
|
||||
const sudoKey = useCall<AccountId>(apiProps.isApiReady && apiProps.api.query.sudo?.key, []);
|
||||
const [hasSudo, setHasSudo] = useState(false);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const count = useCounter();
|
||||
const count = (route.useCounter || DUMMY_COUNTER)();
|
||||
|
||||
useEffect((): void => {
|
||||
setHasSudo(!!sudoKey && allAccounts.some((address): boolean => sudoKey.eq(address)));
|
||||
}, [allAccounts, sudoKey]);
|
||||
|
||||
useEffect((): void => {
|
||||
const isVisible = checkVisible(name, apiProps, hasAccounts, hasSudo, display);
|
||||
const isVisible = checkVisible(route.name, apiProps, hasAccounts, hasSudo, route.display);
|
||||
|
||||
route.isIgnored = !isVisible;
|
||||
setIsVisible(isVisible);
|
||||
}, [apiProps, hasAccounts, hasSudo]);
|
||||
}, [apiProps, hasAccounts, hasSudo, route]);
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { Modal, i18n, icon, name } = route;
|
||||
|
||||
const body = (
|
||||
<>
|
||||
<Icon name={icon} />
|
||||
|
||||
@ -16,6 +16,7 @@ function WarmUp (): React.ReactElement {
|
||||
|
||||
useEffect((): void => {
|
||||
setHasValues(!!fees || !!indexes || !!issuance || !!registrars || !!staking);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@ -15,7 +15,10 @@ interface Props {
|
||||
function AccountSelector ({ className, onChange }: Props): React.ReactElement<Props> {
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
|
||||
useEffect((): void => onChange(accountId), [accountId]);
|
||||
useEffect(
|
||||
(): void => onChange(accountId),
|
||||
[accountId, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<section className={`template--AccountSelector ui--row ${className}`}>
|
||||
|
||||
@ -71,7 +71,7 @@ function Account ({ address, className, filter, isFavorite, toggleFavorite }: Pr
|
||||
} else if (nickname) {
|
||||
setAccName(nickname);
|
||||
}
|
||||
}, [info]);
|
||||
}, [api, info]);
|
||||
|
||||
useEffect((): void => {
|
||||
const account = keyring.getAccount(address);
|
||||
@ -84,7 +84,7 @@ function Account ({ address, className, filter, isFavorite, toggleFavorite }: Pr
|
||||
});
|
||||
_setTags(account?.meta.tags || []);
|
||||
setAccName(account?.meta.name || '');
|
||||
}, [address]);
|
||||
}, [address, _setTags]);
|
||||
|
||||
useEffect((): void => {
|
||||
if (filter.length === 0) {
|
||||
|
||||
@ -92,14 +92,14 @@ function Derive ({ className, from, onClose }: Props): React.ReactElement {
|
||||
const deriveError = deriveValidate(debouncedSuri, source.type);
|
||||
|
||||
if (!deriveError) {
|
||||
const result = source.derive(suri);
|
||||
const result = source.derive(debouncedSuri);
|
||||
|
||||
address = result.address;
|
||||
}
|
||||
|
||||
return { address, deriveError };
|
||||
});
|
||||
}, [debouncedSuri]);
|
||||
}, [debouncedSuri, source]);
|
||||
|
||||
const _onChangeName = (name: string): void =>
|
||||
setName({ isNameValid: !!name.trim(), name });
|
||||
|
||||
@ -36,7 +36,7 @@ function Transfer ({ className, onClose, recipientId: propRecipientId, senderId:
|
||||
senderId && recipientId && setExtrinsic(
|
||||
() => api.tx.balances.transfer(recipientId, amount || ZERO)
|
||||
);
|
||||
}, [amount, recipientId, senderId]);
|
||||
}, [api, amount, recipientId, senderId]);
|
||||
|
||||
const transferrable = <span className='label'>{t('transferrable')}</span>;
|
||||
|
||||
|
||||
@ -45,6 +45,14 @@ function Address ({ address, className, filter, isFavorite, toggleFavorite }: Pr
|
||||
|
||||
const _setTags = (tags: string[]): void => setTags(tags.sort());
|
||||
|
||||
useEffect((): void => {
|
||||
const current = keyring.getAddress(address);
|
||||
|
||||
setCurrent(current || null);
|
||||
setGenesisHash((current && current.meta.genesisHash) || null);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect((): void => {
|
||||
const { identity, nickname } = info || {};
|
||||
|
||||
@ -55,14 +63,7 @@ function Address ({ address, className, filter, isFavorite, toggleFavorite }: Pr
|
||||
} else if (nickname) {
|
||||
setAccName(nickname);
|
||||
}
|
||||
}, [info]);
|
||||
|
||||
useEffect((): void => {
|
||||
const current = keyring.getAddress(address);
|
||||
|
||||
setCurrent(current || null);
|
||||
setGenesisHash((current && current.meta.genesisHash) || null);
|
||||
}, []);
|
||||
}, [api, info]);
|
||||
|
||||
useEffect((): void => {
|
||||
const account = keyring.getAddress(address);
|
||||
|
||||
@ -25,11 +25,11 @@ function Match ({ address, className, count, offset, onCreateToggle, onRemove, s
|
||||
);
|
||||
const _onCreate = useCallback(
|
||||
(): void => onCreateToggle(hexSeed),
|
||||
[hexSeed]
|
||||
[hexSeed, onCreateToggle]
|
||||
);
|
||||
const _onRemove = useCallback(
|
||||
(): void => onRemove(address),
|
||||
[address]
|
||||
[address, onRemove]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { Option } from '@polkadot/types';
|
||||
import { BalanceOf, EthereumAddress } from '@polkadot/types/interfaces';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Button, Card } from '@polkadot/react-components';
|
||||
import { useApi } from '@polkadot/react-hooks';
|
||||
@ -27,24 +27,26 @@ function Claim ({ button, className, ethereumAddress }: Props): React.ReactEleme
|
||||
const [claimAddress, setClaimAddress] = useState<EthereumAddress | null>(null);
|
||||
const [isBusy, setIsBusy] = useState(false);
|
||||
|
||||
const _fetchClaim = (address: EthereumAddress): void => {
|
||||
setIsBusy(true);
|
||||
const _fetchClaim = useCallback(
|
||||
(address: EthereumAddress): void => {
|
||||
setIsBusy(true);
|
||||
|
||||
api.query.claims
|
||||
.claims<Option<BalanceOf>>(address)
|
||||
.then((claim): void => {
|
||||
setClaimValue(claim.unwrapOr(null));
|
||||
setIsBusy(false);
|
||||
})
|
||||
.catch((): void => setIsBusy(false));
|
||||
};
|
||||
api.query.claims
|
||||
.claims<Option<BalanceOf>>(address)
|
||||
.then((claim): void => {
|
||||
setClaimValue(claim.unwrapOr(null));
|
||||
setIsBusy(false);
|
||||
})
|
||||
.catch((): void => setIsBusy(false));
|
||||
},
|
||||
[api]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
if (ethereumAddress !== claimAddress) {
|
||||
setClaimAddress(ethereumAddress);
|
||||
ethereumAddress && _fetchClaim(ethereumAddress);
|
||||
}
|
||||
}, [ethereumAddress]);
|
||||
setClaimAddress(ethereumAddress);
|
||||
|
||||
ethereumAddress && _fetchClaim(ethereumAddress);
|
||||
}, [_fetchClaim, ethereumAddress]);
|
||||
|
||||
if (isBusy || !claimAddress) {
|
||||
return null;
|
||||
|
||||
@ -12,6 +12,6 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2",
|
||||
"@polkadot/api-contract": "^1.9.0-beta.11"
|
||||
"@polkadot/api-contract": "^1.9.0-beta.12"
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,13 +33,8 @@ interface Props extends BareProps {
|
||||
function Call (props: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { className, isOpen, callContract, callMessageIndex, onChangeCallContractAddress, onChangeCallMessageIndex, onClose } = props;
|
||||
|
||||
if (isNull(callContract) || isNull(callMessageIndex)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasRpc = callContract.hasRpcContractsCall;
|
||||
let callMessage = callContract.getMessage(callMessageIndex);
|
||||
const hasRpc = callContract?.hasRpcContractsCall;
|
||||
const callMessage = callContract?.getMessage(isNull(callMessageIndex) ? undefined : callMessageIndex);
|
||||
|
||||
const { api } = useApi();
|
||||
const [accountId, setAccountId] = useState<StringOrNull>(null);
|
||||
@ -51,23 +46,29 @@ function Call (props: Props): React.ReactElement<Props> | null {
|
||||
const [useRpc, setUseRpc] = useState(hasRpc && callMessage && !callMessage.def.mutates);
|
||||
|
||||
useEffect((): void => {
|
||||
callMessage = callContract.getMessage(callMessageIndex);
|
||||
if (callContract && callMessageIndex) {
|
||||
const callMessage = callContract.getMessage(callMessageIndex);
|
||||
|
||||
setParams(callMessage ? callMessage.def.args.map(({ type }): any => createValue({ type })) : []);
|
||||
setParams(callMessage ? callMessage.def.args.map(({ type }): any => createValue({ type })) : []);
|
||||
|
||||
if (hasRpc) {
|
||||
if (!callMessage || callMessage.def.mutates) {
|
||||
setUseRpc(false);
|
||||
} else {
|
||||
setUseRpc(true);
|
||||
if (hasRpc) {
|
||||
if (!callMessage || callMessage.def.mutates) {
|
||||
setUseRpc(false);
|
||||
} else {
|
||||
setUseRpc(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [callContract, callMessageIndex]);
|
||||
}, [callContract, callMessageIndex, hasRpc]);
|
||||
|
||||
useEffect((): void => {
|
||||
setOutcomes([]);
|
||||
}, [callContract]);
|
||||
|
||||
if (isNull(callContract) || isNull(callMessageIndex) || !callMessage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const _onChangeAccountId = (accountId: StringOrNull): void => setAccountId(accountId);
|
||||
|
||||
const _onChangeCallMessageIndexString = (callMessageIndexString: string): void => {
|
||||
@ -91,7 +92,7 @@ function Call (props: Props): React.ReactElement<Props> | null {
|
||||
};
|
||||
|
||||
const _onSubmitRpc = (): void => {
|
||||
if (!accountId) return;
|
||||
if (!accountId || !callMessage) return;
|
||||
|
||||
callContract
|
||||
.call('rpc', callMessage.def.name, endowment, gasLimit, ...params)
|
||||
|
||||
@ -31,13 +31,12 @@ const ContractCard = styled(Card)`
|
||||
function Contract (props: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { contract: { abi, address }, onCall } = props;
|
||||
const [isForgetOpen, setIsForgetOpen] = useState(false);
|
||||
|
||||
if (!address || !abi) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [isForgetOpen, setIsForgetOpen] = useState(false);
|
||||
|
||||
const _toggleForget = (): void => setIsForgetOpen(!isForgetOpen);
|
||||
const _onForget = (): void => {
|
||||
if (!address) {
|
||||
|
||||
@ -39,7 +39,7 @@ function ValidateAddr ({ address, onChange }: Props): React.ReactElement<Props>
|
||||
|
||||
useEffect((): void => {
|
||||
onChange(isAddress && isStored);
|
||||
}, [isAddress, isStored]);
|
||||
}, [isAddress, isStored, onChange]);
|
||||
|
||||
if (isStored || !isAddress) {
|
||||
return null;
|
||||
|
||||
@ -35,13 +35,9 @@ function Contracts (props: Props): React.ReactElement<Props> {
|
||||
|
||||
useEffect((): void => {
|
||||
setContracts(filterContracts(api, props));
|
||||
}, [accounts, api, keyringContracts]);
|
||||
}, [accounts, api, keyringContracts, props]);
|
||||
|
||||
let callContract = contracts[callContractIndex] || null;
|
||||
|
||||
useEffect((): void => {
|
||||
callContract = contracts[callContractIndex];
|
||||
}, [callContractIndex]);
|
||||
const callContract = contracts[callContractIndex] || null;
|
||||
|
||||
const _toggleAdd = (): void => setIsAddOpen(!isAddOpen);
|
||||
const _toggleCall = (): void => setIsCallOpen(!isCallOpen);
|
||||
|
||||
@ -51,8 +51,7 @@ function onSelectConstructor (props: Props, index: number): () => void {
|
||||
};
|
||||
}
|
||||
|
||||
function renderItem (props: Props, message: ContractABIMessage, index: number, asConstructor = false): React.ReactNode {
|
||||
const { t } = useTranslation();
|
||||
function renderItem (props: Props, message: ContractABIMessage, index: number, asConstructor: boolean, t: (key: string) => string): React.ReactNode {
|
||||
const { docs = [], name } = message;
|
||||
|
||||
return (
|
||||
@ -108,24 +107,24 @@ function renderItem (props: Props, message: ContractABIMessage, index: number, a
|
||||
);
|
||||
}
|
||||
|
||||
function renderConstructor (props: Props, index: number): React.ReactNode {
|
||||
function renderConstructor (props: Props, index: number, t: (key: string) => string): React.ReactNode {
|
||||
const { contractAbi: { abi: { contract: { constructors } } } } = props;
|
||||
|
||||
if (!constructors[index]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return renderItem(props, constructors[index], index, true);
|
||||
return renderItem(props, constructors[index], index, true, t);
|
||||
}
|
||||
|
||||
function renderMessage (props: Props, index: number): React.ReactNode {
|
||||
function renderMessage (props: Props, index: number, t: (key: string) => string): React.ReactNode {
|
||||
const { contractAbi: { abi: { contract: { messages } } } } = props;
|
||||
|
||||
if (!messages[index]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return renderItem(props, messages[index], index);
|
||||
return renderItem(props, messages[index], index, false, t);
|
||||
}
|
||||
|
||||
function Messages (props: Props): React.ReactElement<Props> {
|
||||
@ -134,8 +133,8 @@ function Messages (props: Props): React.ReactElement<Props> {
|
||||
|
||||
return (
|
||||
<div className={classes(className, 'ui--Messages', isLabelled && 'labelled')}>
|
||||
{withConstructors && constructors.map((_, index): React.ReactNode => renderConstructor(props, index))}
|
||||
{messages.map((_, index): React.ReactNode => renderMessage(props, index))}
|
||||
{withConstructors && constructors.map((_, index): React.ReactNode => renderConstructor(props, index, t))}
|
||||
{messages.map((_, index): React.ReactNode => renderMessage(props, index, t))}
|
||||
{isRemovable && (
|
||||
<IconLink
|
||||
label={t('Remove ABI')}
|
||||
|
||||
@ -37,7 +37,7 @@ function ProposeExternal ({ className, isMember, members }: Props): React.ReactE
|
||||
} else {
|
||||
setProposal(null);
|
||||
}
|
||||
}, [hash, isHashValid]);
|
||||
}, [api, hash, isHashValid]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -49,7 +49,7 @@ function Slashing ({ className, isMember, members }: Props): React.ReactElement<
|
||||
} else {
|
||||
setEras([]);
|
||||
}
|
||||
}, [slashes]);
|
||||
}, [slashes, t]);
|
||||
|
||||
useEffect((): void => {
|
||||
const actioned = selectedEra && slashes?.find(([era]): boolean => era.eqn(selectedEra));
|
||||
@ -59,7 +59,7 @@ function Slashing ({ className, isMember, members }: Props): React.ReactElement<
|
||||
? api.tx.staking.cancelDeferredSlash(actioned[0], actioned[1].map((_, index): number => index))
|
||||
: null
|
||||
);
|
||||
}, [selectedEra, slashes]);
|
||||
}, [api, selectedEra, slashes]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -48,7 +48,7 @@ export default function useAvailableSlashes (): [BN, UnappliedSlash[]][] {
|
||||
return (): void => {
|
||||
unsub && unsub();
|
||||
};
|
||||
}, [earliestSlash, indexes]);
|
||||
}, [api, earliestSlash, indexes, mountedRef]);
|
||||
|
||||
return slashes;
|
||||
}
|
||||
|
||||
@ -60,9 +60,11 @@ function Overview ({ className, prime }: Props): React.ReactElement<Props> {
|
||||
const bestNumber = useCall<BlockNumber>(api.derive.chain.bestNumber, []);
|
||||
const electionsInfo = useCall<DerivedElectionsInfo>(api.derive.elections.info, []);
|
||||
const allVotes = api.query.electionsPhragmen?.votesOf.creator.meta.type.asMap.linked.isTrue
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
? useCall<Record<string, AccountId[]>>(api.query.electionsPhragmen?.votesOf, [], {
|
||||
transform: transformVotesPrev
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
: useCall<Record<string, AccountId[]>>(api.query.electionsPhragmen?.votesOf.entries as any, [], {
|
||||
transform: transformVotes
|
||||
});
|
||||
|
||||
@ -33,7 +33,7 @@ function DispatchEntry ({ blockNumber, hash, referendumIndex }: Props): React.Re
|
||||
|
||||
useEffect((): void => {
|
||||
preimage?.isSome && setProposal(api.createType('Proposal', preimage.unwrap()[0].toU8a(true)));
|
||||
}, [preimage]);
|
||||
}, [api, preimage]);
|
||||
|
||||
return (
|
||||
<tr>
|
||||
|
||||
@ -49,7 +49,7 @@ function Externals ({ className }: Props): React.ReactElement<Props> | null {
|
||||
} else {
|
||||
setExpanded(null);
|
||||
}
|
||||
}, [preimage]);
|
||||
}, [api, preimage]);
|
||||
|
||||
if (!hash) {
|
||||
return null;
|
||||
|
||||
@ -46,7 +46,7 @@ function Fasttrack ({ hash, threshold }: Props): React.ReactElement<Props> | nul
|
||||
? api.tx.democracy.fastTrack(hash, votingBlocks, delayBlocks)
|
||||
: null
|
||||
);
|
||||
}, [delayBlocks, hash, votingBlocks]);
|
||||
}, [api, delayBlocks, hash, votingBlocks]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -17,7 +17,7 @@ export default function useCounter (): number {
|
||||
(proposals?.length || 0) +
|
||||
(referenda?.length || 0)
|
||||
);
|
||||
}, [proposals, referenda]);
|
||||
}, [mountedRef, proposals, referenda]);
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
@ -20,11 +20,13 @@ function Entry (): React.ReactElement<{}> | null {
|
||||
const [stateValue, setStateValue] = useState<string | undefined>(value);
|
||||
|
||||
useEffect((): void => {
|
||||
if (value && value !== stateValue) {
|
||||
setStateValue(value);
|
||||
} else if (!stateValue && bestNumber) {
|
||||
setStateValue(bestNumber.toString());
|
||||
}
|
||||
setStateValue((stateValue) =>
|
||||
value && value !== stateValue
|
||||
? value
|
||||
: !stateValue && bestNumber
|
||||
? bestNumber.toString()
|
||||
: stateValue
|
||||
);
|
||||
}, [bestNumber, value]);
|
||||
|
||||
if (!stateValue) {
|
||||
|
||||
@ -352,6 +352,7 @@ function Forks ({ className }: Props): React.ReactElement<Props> | null {
|
||||
_subFinHead && _subFinHead();
|
||||
_subNewHead && _subNewHead();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!tree) {
|
||||
|
||||
@ -51,6 +51,7 @@ function NodeInfo (): React.ReactElement<{}> {
|
||||
return (): void => {
|
||||
window.clearInterval(timerId);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@ -55,7 +55,7 @@ function Transfer ({ assets, className, onClose, recipientId: propRecipientId, s
|
||||
? api.tx.genericAsset.transfer(assetId, recipientId, amount)
|
||||
: null
|
||||
);
|
||||
}, [amount, assetId, recipientId, senderId]);
|
||||
}, [api, amount, assetId, recipientId, senderId]);
|
||||
|
||||
const _onAddAssetId = (id: string): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
|
||||
|
||||
@ -106,6 +106,7 @@ function Playground ({ className }: Props): React.ReactElement<Props> {
|
||||
setIsCustomExample((selected && selected.type === 'custom') || false);
|
||||
setOptions(options);
|
||||
setSelected(selected || snippets[0]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect((): void => {
|
||||
|
||||
@ -56,7 +56,7 @@ function Register ({ nextFreeId = ONE_THOUSAND, sudoKey }: Props): React.ReactEl
|
||||
(): ParaInfo => {
|
||||
return createType(api.registry, 'ParaInfo', { scheduling });
|
||||
},
|
||||
[scheduling]
|
||||
[api, scheduling]
|
||||
);
|
||||
const extrinsic = useMemo(
|
||||
(): SubmittableExtrinsic | null => {
|
||||
@ -72,7 +72,7 @@ function Register ({ nextFreeId = ONE_THOUSAND, sudoKey }: Props): React.ReactEl
|
||||
return null;
|
||||
}
|
||||
},
|
||||
[id, info, code, initialHeadState]
|
||||
[api, id, info, code, initialHeadState]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -37,7 +37,7 @@ function Deregister ({ id, info, isOpen, onClose, onSubmit, sudoKey }: Props): R
|
||||
return null;
|
||||
}
|
||||
},
|
||||
[id]
|
||||
[api, id]
|
||||
);
|
||||
|
||||
const onStart = (): void => {
|
||||
|
||||
@ -39,7 +39,7 @@ function ParachainsApp ({ basePath }: Props): React.ReactElement<Props> {
|
||||
}]
|
||||
: []
|
||||
)
|
||||
], [location, paraInfoRef.current, t]);
|
||||
], [match, t]);
|
||||
|
||||
return (
|
||||
<main>
|
||||
|
||||
@ -31,7 +31,7 @@ function Extensions (): React.ReactElement {
|
||||
.then(() => toggleBusy());
|
||||
}
|
||||
},
|
||||
[extensions, metaDef, selectedIndex]
|
||||
[extensions, metaDef, selectedIndex, toggleBusy]
|
||||
);
|
||||
|
||||
if (!options.length) {
|
||||
|
||||
@ -71,6 +71,9 @@ function SelectUrl ({ className, onChange }: Props): React.ReactElement<Props> {
|
||||
|
||||
useEffect((): void => {
|
||||
onChange && info.isValid && onChange(info.url);
|
||||
// the issue here is that the onChange callback changes each and every render... so Houston, we have
|
||||
// a desperate issue here :(
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [info]);
|
||||
|
||||
const _onChangeUrl = useCallback(
|
||||
@ -87,7 +90,7 @@ function SelectUrl ({ className, onChange }: Props): React.ReactElement<Props> {
|
||||
),
|
||||
isCustom
|
||||
}),
|
||||
[]
|
||||
[info]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -63,7 +63,7 @@ function InjectKeys ({ onClose }: Props): React.ReactElement<Props> | null {
|
||||
rpc: { section: 'author', method: 'insertKey' } as any,
|
||||
values: [keyType, suri, publicKey]
|
||||
}),
|
||||
[keyType, publicKey, suri]
|
||||
[keyType, publicKey, queueRpc, suri]
|
||||
);
|
||||
const _cryptoOptions = useMemo(
|
||||
() => CRYPTO_MAP[keyType].map((value): { text: string; value: KeypairType } => ({
|
||||
|
||||
@ -33,12 +33,10 @@ function ValidateAmount ({ accountId, onError, value }: Props): React.ReactEleme
|
||||
newError = t('The specified value is greater than your free balance. The node will bond the maximum amount available.');
|
||||
}
|
||||
|
||||
if (error !== newError) {
|
||||
onError(newError);
|
||||
setError(newError);
|
||||
}
|
||||
onError(newError);
|
||||
setError((error) => error !== newError ? newError : error);
|
||||
}
|
||||
}, [allBalances, value]);
|
||||
}, [allBalances, onError, t, value]);
|
||||
|
||||
if (!error) {
|
||||
return null;
|
||||
|
||||
@ -49,12 +49,10 @@ function ValidateController ({ accountId, controllerId, defaultController, onErr
|
||||
newError = t('A controller account should not be set to manages multiple stashes. The selected controller is already controlling {{stashId}}', { replace: { stashId } });
|
||||
}
|
||||
|
||||
if (error !== newError) {
|
||||
onError(newError);
|
||||
setError(newError);
|
||||
}
|
||||
onError(newError);
|
||||
setError((error) => error !== newError ? newError : error);
|
||||
}
|
||||
}, [accountId, bondedId, controllerId, defaultController, stashId]);
|
||||
}, [accountId, bondedId, controllerId, defaultController, onError, stashId, t]);
|
||||
|
||||
if (!error || !accountId) {
|
||||
return null;
|
||||
|
||||
@ -21,17 +21,15 @@ function ValidateSessionEd25519 ({ onError, sessionId, stashId }: Props): React.
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect((): void => {
|
||||
let newError = null;
|
||||
let newError: string | null = null;
|
||||
|
||||
if (sessionId === stashId) {
|
||||
newError = t('For fund security, your session key should not match your stash key.');
|
||||
}
|
||||
|
||||
if (error !== newError) {
|
||||
onError(newError);
|
||||
setError(newError);
|
||||
}
|
||||
}, [sessionId, stashId]);
|
||||
onError(newError);
|
||||
setError((error) => error !== newError ? newError : error);
|
||||
}, [onError, sessionId, stashId, t]);
|
||||
|
||||
if (!error) {
|
||||
return null;
|
||||
|
||||
@ -19,7 +19,7 @@ function InputValidationUnstakeThreshold ({ onError, unstakeThreshold }: Props):
|
||||
|
||||
useEffect((): void => {
|
||||
if (unstakeThreshold) {
|
||||
let newError = null;
|
||||
let newError: string | null = null;
|
||||
|
||||
if (unstakeThreshold.ltn(0)) {
|
||||
newError = t('The Threshold must be a positive number');
|
||||
@ -27,12 +27,10 @@ function InputValidationUnstakeThreshold ({ onError, unstakeThreshold }: Props):
|
||||
newError = t('The Threshold must lower than 11');
|
||||
}
|
||||
|
||||
if (newError !== error) {
|
||||
onError(newError);
|
||||
setError(newError);
|
||||
}
|
||||
onError(newError);
|
||||
setError((error) => error !== newError ? newError : error);
|
||||
}
|
||||
}, [unstakeThreshold]);
|
||||
}, [onError, t, unstakeThreshold]);
|
||||
|
||||
if (!error) {
|
||||
return null;
|
||||
|
||||
@ -146,7 +146,7 @@ function Account ({ allStashes, className, isOwnStash, next, onUpdateType, rewar
|
||||
onUpdateType(stashId, 'other');
|
||||
}
|
||||
}
|
||||
}, [allStashes, stakingAccount, stashId, validateInfo]);
|
||||
}, [allAccounts, allStashes, onUpdateType, stakingAccount, stashId, validateInfo]);
|
||||
|
||||
useEffect((): void => {
|
||||
nominees && setActiveNoms(
|
||||
@ -167,7 +167,7 @@ function Account ({ allStashes, className, isOwnStash, next, onUpdateType, rewar
|
||||
accountId: controllerId,
|
||||
extrinsic: createPayout(api, payoutRewards)
|
||||
}),
|
||||
[api, controllerId, payoutRewards]
|
||||
[api, controllerId, payoutRewards, queueExtrinsic]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -43,7 +43,7 @@ export default function useInactives (stashId: string, nominees?: string[]): str
|
||||
return (): void => {
|
||||
unsub && unsub();
|
||||
};
|
||||
}, [indexes, nominees, stashId]);
|
||||
}, [api, indexes, mountedRef, nominees, stashId]);
|
||||
|
||||
return inactives;
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ interface Props {
|
||||
function Favorite ({ address, isFavorite, toggleFavorite }: Props): React.ReactElement<Props> {
|
||||
const _onFavorite = useCallback(
|
||||
(): void => toggleFavorite(address),
|
||||
[address]
|
||||
[address, toggleFavorite]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -115,13 +115,13 @@ function Address ({ address, className, filterName, hasQueries, isAuthor, isElec
|
||||
setNominators && setNominators(info.nominators.map(([who]): string => who.toString()));
|
||||
setStakingState(info);
|
||||
}
|
||||
}, [stakingInfo]);
|
||||
}, [setNominators, stakingInfo, withNominations]);
|
||||
|
||||
useEffect((): void => {
|
||||
setIsVisible(
|
||||
checkVisibility(api, address, filterName, info)
|
||||
);
|
||||
}, [address, filterName, info]);
|
||||
}, [address, api, filterName, info]);
|
||||
|
||||
const _onQueryStats = useCallback(
|
||||
(): void => {
|
||||
|
||||
@ -62,7 +62,7 @@ function ChartRewards ({ validatorId }: Props): React.ReactElement<Props> {
|
||||
const { currency, divisor } = useMemo((): { currency: string; divisor: BN } => ({
|
||||
currency: formatBalance.getDefaults().unit,
|
||||
divisor: new BN('1'.padEnd(formatBalance.getDefaults().decimals + 1, '0'))
|
||||
}), [formatBalance]);
|
||||
}), []);
|
||||
const legends = useMemo(() => [
|
||||
t('{{currency}} slashed', { replace: { currency } }),
|
||||
t('{{currency}} rewards', { replace: { currency } }),
|
||||
@ -73,7 +73,7 @@ function ChartRewards ({ validatorId }: Props): React.ReactElement<Props> {
|
||||
erasRewards && ownSlashes && stakerPoints && setChart(
|
||||
extractRewards(erasRewards, ownSlashes, stakerPoints, divisor)
|
||||
);
|
||||
}, [erasRewards, stakerPoints]);
|
||||
}, [divisor, erasRewards, ownSlashes, stakerPoints]);
|
||||
|
||||
return (
|
||||
<div className='staking--Chart'>
|
||||
|
||||
@ -54,7 +54,7 @@ function ChartStake ({ validatorId }: Props): React.ReactElement<Props> {
|
||||
const { currency, divisor } = useMemo((): { currency: string; divisor: BN } => ({
|
||||
currency: formatBalance.getDefaults().unit,
|
||||
divisor: new BN('1'.padEnd(formatBalance.getDefaults().decimals + 1, '0'))
|
||||
}), [formatBalance]);
|
||||
}), []);
|
||||
const legends = useMemo(() => [
|
||||
t('{{currency}} clipped', { replace: { currency } }),
|
||||
t('{{currency}} total', { replace: { currency } }),
|
||||
@ -65,7 +65,7 @@ function ChartStake ({ validatorId }: Props): React.ReactElement<Props> {
|
||||
ownExposure && setChart(
|
||||
extractStake(ownExposure, divisor)
|
||||
);
|
||||
}, [ownExposure]);
|
||||
}, [divisor, ownExposure]);
|
||||
|
||||
return (
|
||||
<div className='staking--Chart'>
|
||||
|
||||
@ -31,11 +31,11 @@ export default function useBlockCounts (accountId: string, sessionRewards: Sessi
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [accountId, sessionRewards]);
|
||||
}, [accountId, api, mountedRef, sessionRewards]);
|
||||
|
||||
useEffect((): void => {
|
||||
setCounts([...historic, current || api.createType('u32')].slice(1));
|
||||
}, [current, historic]);
|
||||
}, [api, current, historic]);
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
@ -195,7 +195,7 @@ function Targets ({ className }: Props): React.ReactElement<Props> {
|
||||
? !sortFromMax
|
||||
: true
|
||||
})),
|
||||
[sortBy]
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
|
||||
@ -31,8 +31,8 @@
|
||||
"homepage": "https://github.com/polkadot-js/ui/tree/master/packages/ui-reactive#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2",
|
||||
"@polkadot/api": "^1.9.0-beta.11",
|
||||
"@polkadot/extension-dapp": "^0.23.0-beta.10",
|
||||
"@polkadot/api": "^1.9.0-beta.12",
|
||||
"@polkadot/extension-dapp": "^0.23.1",
|
||||
"rxjs-compat": "^6.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,6 +172,7 @@ function Api ({ children, url }: Props): React.ReactElement<Props> | null {
|
||||
.catch((error) => console.error(error));
|
||||
|
||||
setIsApiInitialized(true);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!props.isApiInitialized) {
|
||||
|
||||
@ -199,7 +199,7 @@ function AccountName ({ children, className, defaultName, label, onClick, overri
|
||||
} else {
|
||||
setName(defaultOrAddr(defaultName, cacheAddr, accountIndex));
|
||||
}
|
||||
}, [address, info, isRegistrar, toggle]);
|
||||
}, [api, address, defaultName, info, isRegistrar, t, toggle, toggleJudgement]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -263,9 +263,5 @@ export default React.memo(styled(AccountName)`
|
||||
line-height: 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.ui--Badge {
|
||||
margin-top: -2px;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
@ -87,7 +87,7 @@ function ChartHorizBar ({ aspectRatio = 8, className, max = 100, showLabels = fa
|
||||
if (newJsonValues !== jsonValues) {
|
||||
setState(calculateOptions(aspectRatio, values, newJsonValues, max, showLabels));
|
||||
}
|
||||
}, [values]);
|
||||
}, [aspectRatio, jsonValues, max, showLabels, values]);
|
||||
|
||||
if (!chartData) {
|
||||
return null;
|
||||
|
||||
@ -82,7 +82,7 @@ function LineChart ({ className, colors, labels, legends, style, values }: LineP
|
||||
if (newJsonValues !== jsonValues) {
|
||||
setState(calculateOptions(colors, legends, labels, values, newJsonValues));
|
||||
}
|
||||
}, [labels, legends, values]);
|
||||
}, [colors, jsonValues, labels, legends, values]);
|
||||
|
||||
if (!chartData) {
|
||||
return null;
|
||||
|
||||
@ -56,6 +56,7 @@ function BaseDropdown<Option> ({ allowAdd = false, className, defaultValue, drop
|
||||
lastUpdate.current = json;
|
||||
|
||||
setStored(value);
|
||||
|
||||
onChange && onChange(
|
||||
transform
|
||||
? transform(value)
|
||||
@ -68,7 +69,7 @@ function BaseDropdown<Option> ({ allowAdd = false, className, defaultValue, drop
|
||||
|
||||
useEffect((): void => {
|
||||
_setStored(isUndefined(value) ? defaultValue : value);
|
||||
}, [defaultValue, value]);
|
||||
}, [_setStored, defaultValue, value]);
|
||||
|
||||
const _onAdd = (_: React.SyntheticEvent<HTMLElement>, { value }: DropdownProps): void =>
|
||||
onAdd && onAdd(value);
|
||||
|
||||
@ -50,6 +50,7 @@ function Editor ({ className, code, isValid, onEdit }: Props): React.ReactElemen
|
||||
});
|
||||
|
||||
editorRef.current = editor;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect((): void => {
|
||||
|
||||
@ -61,7 +61,7 @@ function ExtrinsicDisplay ({ defaultValue, isDisabled, isError, isPrivate, label
|
||||
}
|
||||
|
||||
onChange(method);
|
||||
}, [extrinsic, values]);
|
||||
}, [extrinsic, onChange, values]);
|
||||
|
||||
const _onChangeMethod = (fn: SubmittableExtrinsicFunction<'promise'>): void => setCall({ fn, params: getParams(fn) });
|
||||
|
||||
|
||||
@ -21,16 +21,16 @@ interface Props extends BareProps {
|
||||
}
|
||||
|
||||
function SelectMethod ({ api, className, isError, onChange, options, style, value }: Props): React.ReactElement<Props> | null {
|
||||
if (!options.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const transform = useCallback(
|
||||
(method: string): SubmittableExtrinsicFunction<'promise'> =>
|
||||
api.tx[value.section][method],
|
||||
[value]
|
||||
[api, value]
|
||||
);
|
||||
|
||||
if (!options.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
className={classes('ui--DropdownLinked-Items', className)}
|
||||
|
||||
@ -206,15 +206,19 @@ function InputNumber (props: Props): React.ReactElement<Props> {
|
||||
|
||||
useEffect((): void => {
|
||||
propsValue && isNewPropsValue(propsValue, value, valueBn) && setValues(getValues(propsValue, si, props));
|
||||
// ummmm...
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [propsValue]);
|
||||
|
||||
useEffect((): void => {
|
||||
setValues(getValues(value, si, props));
|
||||
// ummmm...
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [value, si, bitLength, maxValue]);
|
||||
|
||||
useEffect((): void => {
|
||||
onChange && onChange(valueBn);
|
||||
}, [valueBn]);
|
||||
}, [onChange, valueBn]);
|
||||
|
||||
const _onChange = (input: string): void => {
|
||||
setValues(getValuesFromString(input, si, props));
|
||||
|
||||
@ -46,6 +46,7 @@ function Tooltip ({ className, effect = 'solid', offset, place = 'top', text, tr
|
||||
rootElement.removeChild(tooltipContainer);
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
|
||||
@ -38,7 +38,7 @@ function TreasuryProposal ({ className, asInset, insetProps, onClick, proposal,
|
||||
} else {
|
||||
setProposal(proposal || null);
|
||||
}
|
||||
}, [proposal, proposalId]);
|
||||
}, [api, proposal, proposalId]);
|
||||
|
||||
if (!stateProposal) {
|
||||
return null;
|
||||
|
||||
@ -54,7 +54,7 @@ function VoteValue ({ accountId, autoFocus, onChange }: Props): React.ReactEleme
|
||||
// only do onChange to parent when the BN value comes in, not our formatted version
|
||||
useEffect((): void => {
|
||||
isBn(value) && onChange(value);
|
||||
}, [value]);
|
||||
}, [onChange, value]);
|
||||
|
||||
const _setValue = useCallback(
|
||||
(value?: BN): void => setValue({ selectedId, value }),
|
||||
|
||||
@ -20,4 +20,3 @@ export { default as usePassword } from './usePassword';
|
||||
export { default as useRegistrars } from './useRegistrars';
|
||||
export { default as useSudo } from './useSudo';
|
||||
export { default as useToggle } from './useToggle';
|
||||
export { default as useTx } from './useTx';
|
||||
|
||||
@ -8,13 +8,14 @@ import { useCallback, useState } from 'react';
|
||||
|
||||
export default function useAccountId (initialValue: StringOrNull = null, onChangeAccountId?: (_: StringOrNull) => void): [StringOrNull, (_: StringOrNull) => void] {
|
||||
const [accountId, setAccountId] = useState<StringOrNull>(initialValue);
|
||||
|
||||
const _setAccountId = useCallback(
|
||||
(accountId: StringOrNull = null): void => {
|
||||
setAccountId(accountId);
|
||||
|
||||
onChangeAccountId && onChangeAccountId(accountId);
|
||||
},
|
||||
[]
|
||||
[onChangeAccountId]
|
||||
);
|
||||
|
||||
return [accountId, _setAccountId];
|
||||
|
||||
@ -29,6 +29,7 @@ export default function useAccounts (): UseAccounts {
|
||||
return (): void => {
|
||||
setTimeout(() => subscription.unsubscribe(), 0);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return state;
|
||||
|
||||
@ -29,6 +29,7 @@ export default function useAccounts (): UseAccounts {
|
||||
return (): void => {
|
||||
setTimeout(() => subscription.unsubscribe(), 0);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return state;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import store from 'store';
|
||||
import useApi from './useApi';
|
||||
@ -10,9 +10,12 @@ import useApi from './useApi';
|
||||
// create a chain-specific key for the local cache
|
||||
export default function useCacheKey <T> (storageKeyBase: string): [(defaultValue?: T) => T | undefined, (value: T) => T] {
|
||||
const { api, isDevelopment } = useApi();
|
||||
const STORAGE_KEY = `${storageKeyBase}:${isDevelopment ? 'development' : api.genesisHash}`;
|
||||
const getter = useCallback((): T | undefined => store.get(STORAGE_KEY), []);
|
||||
const setter = useCallback((value: T): T => store.set(STORAGE_KEY, value), []);
|
||||
const storageKey = useMemo(
|
||||
() => `${storageKeyBase}:${isDevelopment ? 'development' : api.genesisHash}`,
|
||||
[api, isDevelopment, storageKeyBase]
|
||||
);
|
||||
const getter = useCallback((): T | undefined => store.get(storageKey), [storageKey]);
|
||||
const setter = useCallback((value: T): T => store.set(storageKey, value), [storageKey]);
|
||||
|
||||
return [getter, setter];
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ export default function useCall <T> (fn: TrackFn | undefined | null | false, par
|
||||
subscribe(mountedRef, tracker, fn, mappedParams, setValue, options);
|
||||
}
|
||||
}
|
||||
}, [fn, params]);
|
||||
}, [fn, options, mountedRef, params]);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -6,21 +6,23 @@ import { useState, useEffect } from 'react';
|
||||
|
||||
import useIsMountedRef from './useIsMountedRef';
|
||||
|
||||
const DEFAULT_DELAY = 250;
|
||||
|
||||
// Debounces inputs
|
||||
export default function useDebounce <T> (value: T, delay = 250): T {
|
||||
export default function useDebounce <T> (value: T, delay?: number): T {
|
||||
const mountedRef = useIsMountedRef();
|
||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||
|
||||
useEffect((): () => void => {
|
||||
const handler = setTimeout(() => {
|
||||
mountedRef.current && setDebouncedValue(value);
|
||||
}, delay);
|
||||
}, delay || DEFAULT_DELAY);
|
||||
|
||||
// each time it renders, it clears
|
||||
return (): void => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [value]);
|
||||
}, [delay, value, mountedRef]);
|
||||
|
||||
return debouncedValue;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import useCacheKey from './useCacheKey';
|
||||
export default function useFavorites (storageKeyBase: string): [string[], (address: string) => void] {
|
||||
const [getCache, setCache] = useCacheKey<string[]>(storageKeyBase);
|
||||
const [favorites, setFavorites] = useState<string[]>(getCache() || []);
|
||||
|
||||
const toggleFavorite = useCallback(
|
||||
(address: string): void => setFavorites(
|
||||
(favorites: string[]) => setCache(
|
||||
@ -18,7 +19,7 @@ export default function useFavorites (storageKeyBase: string): [string[], (addre
|
||||
: [...favorites, address]
|
||||
)
|
||||
),
|
||||
[]
|
||||
[setCache]
|
||||
);
|
||||
|
||||
return [favorites, toggleFavorite];
|
||||
|
||||
@ -18,7 +18,7 @@ export default function useFormField<T> (defaultValue: T | null, validate: (_?:
|
||||
const [value, setValue] = useState<T | null>(defaultValue);
|
||||
const isValid = useMemo(
|
||||
(): boolean => isTruthy<T>(value) && validate(value),
|
||||
[value]
|
||||
[validate, value]
|
||||
);
|
||||
const setter = useCallback((value?: T | null): void => setValue(value || null), []);
|
||||
|
||||
|
||||
@ -4,9 +4,7 @@
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export interface MountedRef {
|
||||
current: boolean;
|
||||
}
|
||||
export type MountedRef = React.MutableRefObject<boolean>;
|
||||
|
||||
export default function useIsMountedRef (): MountedRef {
|
||||
const isMounted = useRef(false);
|
||||
|
||||
@ -18,10 +18,12 @@ export default function useMembers (collective: 'council' | 'technicalCommittee'
|
||||
const [state, setState] = useState<Result>({ isMember: false, members: [] });
|
||||
const retrieved = (
|
||||
collective === 'council'
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
? useCall<string[]>(api.query.electionsPhragmen?.members || api.query.elections.members, [], {
|
||||
transform: (accounts: [AccountId, Balance][]): string[] =>
|
||||
accounts.map(([accountId]) => accountId.toString())
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
: useCall<string[]>(api.query.technicalCommittee.members, [], {
|
||||
transform: (accounts: AccountId[]): string[] =>
|
||||
accounts.map((accountId) => accountId.toString())
|
||||
|
||||
@ -16,7 +16,7 @@ export default function useModal (defaultIsOpen?: boolean, onOpen?: () => void,
|
||||
|
||||
onOpen && onOpen();
|
||||
},
|
||||
[]
|
||||
[onOpen, setIsOpen]
|
||||
);
|
||||
const _onClose = useCallback(
|
||||
(): void => {
|
||||
@ -24,7 +24,7 @@ export default function useModal (defaultIsOpen?: boolean, onOpen?: () => void,
|
||||
|
||||
onClose && onClose();
|
||||
},
|
||||
[]
|
||||
[onClose, setIsOpen]
|
||||
);
|
||||
|
||||
return { isOpen, onClose: _onClose, onOpen: _onOpen };
|
||||
|
||||
@ -41,7 +41,7 @@ function useNextPayouts (onlyLatest?: boolean): [string, BN][] | undefined {
|
||||
: lastPayouts
|
||||
);
|
||||
}
|
||||
}, [allInfo, indexes, onlyLatest, stashIds]);
|
||||
}, [allInfo, indexes, mountedRef, onlyLatest, stashIds]);
|
||||
|
||||
return nextPayouts;
|
||||
}
|
||||
@ -75,7 +75,7 @@ export default function useOwnEraRewards (onlyLatest?: boolean): OwnRewards {
|
||||
mountedRef.current && available && nextPayouts && setState(
|
||||
getRewards(available, nextPayouts)
|
||||
);
|
||||
}, [available, nextPayouts]);
|
||||
}, [available, mountedRef, nextPayouts]);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ export default function useOwnStashes (): [string, IsInKeyring][] | undefined {
|
||||
mountedRef.current && allAccounts && ownBonded && ownLedger && setState(
|
||||
getStashes(allAccounts, ownBonded, ownLedger)
|
||||
);
|
||||
}, [allAccounts, ownBonded, ownLedger]);
|
||||
}, [allAccounts, mountedRef, ownBonded, ownLedger]);
|
||||
|
||||
return state;
|
||||
}
|
||||
@ -58,7 +58,7 @@ export function useOwnStashIds (): string[] | undefined {
|
||||
mountedRef.current && ownStashes && setStashIds(
|
||||
ownStashes.map(([stashId]) => stashId)
|
||||
);
|
||||
}, [ownStashes]);
|
||||
}, [mountedRef, ownStashes]);
|
||||
|
||||
return stashIds;
|
||||
}
|
||||
|
||||
@ -1,115 +0,0 @@
|
||||
// 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 { StringOrNull } from '@polkadot/react-components/types';
|
||||
import { Call } from '@polkadot/types/interfaces';
|
||||
import { ExtrinsicAndSenders, TxDef, TxDefs, TxSource, TxProps, TxState } from './types';
|
||||
|
||||
import { useContext, useMemo, useState } from 'react';
|
||||
import { ApiPromise } from '@polkadot/api';
|
||||
import { StatusContext } from '@polkadot/react-components/Status';
|
||||
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
|
||||
import { assert, isFunction } from '@polkadot/util';
|
||||
import useApi from './useApi';
|
||||
|
||||
function getExtrinsic<T extends TxDefs> (api: ApiPromise, txDef: T): SubmittableExtrinsic | null {
|
||||
if (!txDef) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Array.isArray(txDef)) {
|
||||
const [section, method] = txDef[0].split('.');
|
||||
|
||||
assert(api.tx[section] && api.tx[section][method], `Unable to find api.tx.${section}.${method}`);
|
||||
|
||||
try {
|
||||
return api.tx[section][method](...(
|
||||
isFunction(txDef[1])
|
||||
? txDef[1]()
|
||||
: txDef[1]
|
||||
)) as any as SubmittableExtrinsic;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if ((txDef as Call).callIndex) {
|
||||
const fn = api.findCall(txDef.callIndex);
|
||||
|
||||
return api.tx[fn.section][fn.method](...txDef.args);
|
||||
}
|
||||
|
||||
return txDef as any as SubmittableExtrinsic;
|
||||
}
|
||||
}
|
||||
|
||||
export default function useTx<T extends TxDef> (memoFn: (...args: any[]) => TxSource<T>, memoArr: any[], { accountId: anAccountId, onChangeAccountId, onStart, onSuccess, onFailed, onUpdate }: TxProps = {}): TxState {
|
||||
const { queueExtrinsic } = useContext(StatusContext);
|
||||
const { api } = useApi();
|
||||
const txSource = useMemo(memoFn, memoArr);
|
||||
const [accountId, setAccountId] = useState<StringOrNull>(anAccountId || null);
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
|
||||
const _onStart = (): void => {
|
||||
setIsSending(true);
|
||||
|
||||
onStart && onStart();
|
||||
};
|
||||
|
||||
const _onSuccess = (): void => {
|
||||
setIsSending(false);
|
||||
|
||||
onSuccess && onSuccess();
|
||||
};
|
||||
|
||||
const _onFailed = (): void => {
|
||||
setIsSending(false);
|
||||
|
||||
onFailed && onFailed();
|
||||
};
|
||||
|
||||
const _onUpdate = (): void => {
|
||||
setIsSending(false);
|
||||
|
||||
onUpdate && onUpdate();
|
||||
};
|
||||
|
||||
function getExtrinsicAndSenders (api: ApiPromise, accountId: StringOrNull, [txDef, isSubmittable]: TxSource<T>): ExtrinsicAndSenders {
|
||||
const extrinsic = getExtrinsic<T>(api, txDef);
|
||||
|
||||
function _sendTx (isUnsigned?: boolean): void {
|
||||
!!extrinsic && isSubmittable && queueExtrinsic({
|
||||
accountId,
|
||||
extrinsic,
|
||||
isUnsigned,
|
||||
txFailedCb: _onFailed,
|
||||
txStartCb: _onStart,
|
||||
txSuccessCb: _onSuccess,
|
||||
txUpdateCb: _onUpdate
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
extrinsic,
|
||||
isSubmittable: !!accountId && !!extrinsic && isSubmittable,
|
||||
sendTx: (): void => _sendTx(),
|
||||
sendUnsigned: (): void => _sendTx(true)
|
||||
};
|
||||
}
|
||||
|
||||
const txAndSenders = useMemo<ExtrinsicAndSenders>(
|
||||
(): ExtrinsicAndSenders => getExtrinsicAndSenders(api, accountId, txSource),
|
||||
[accountId, txSource]
|
||||
);
|
||||
|
||||
return {
|
||||
...txAndSenders,
|
||||
isSending,
|
||||
accountId,
|
||||
onChangeAccountId: (accountId: StringOrNull): void => {
|
||||
setAccountId(accountId);
|
||||
|
||||
onChangeAccountId && onChangeAccountId(accountId);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -18,7 +18,7 @@ function Balance ({ className, defaultValue: { value }, isDisabled, isError, lab
|
||||
isValid: !isError && !!value,
|
||||
value
|
||||
}),
|
||||
[isError]
|
||||
[isError, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -55,7 +55,7 @@ function BaseBytes ({ asHex, children, className, defaultValue: { value }, isDis
|
||||
: true
|
||||
)
|
||||
);
|
||||
}, [length, value]);
|
||||
}, [length, validate, value]);
|
||||
|
||||
const _onChange = useCallback(
|
||||
(hex: string): void => {
|
||||
@ -80,7 +80,7 @@ function BaseBytes ({ asHex, children, className, defaultValue: { value }, isDis
|
||||
|
||||
setIsValid(isValid);
|
||||
},
|
||||
[withLength]
|
||||
[asHex, length, onChange, validate, withLength]
|
||||
);
|
||||
|
||||
const defaultValue = value
|
||||
|
||||
@ -25,7 +25,7 @@ function Bytes ({ className, defaultValue, isDisabled, isError, label, name, onC
|
||||
value: Compact.addLengthPrefix(value)
|
||||
});
|
||||
},
|
||||
[]
|
||||
[onChange]
|
||||
);
|
||||
|
||||
return !isDisabled && isFileDrop
|
||||
|
||||
@ -38,7 +38,7 @@ function ErrorDisplay (props: Props): React.ReactElement<Props> {
|
||||
setDetails({ details: null });
|
||||
}
|
||||
}
|
||||
}, [props.defaultValue]);
|
||||
}, [details, props.defaultValue]);
|
||||
|
||||
if (!props.isDisabled || !details) {
|
||||
return <Unknown {...props} />;
|
||||
|
||||
@ -70,7 +70,7 @@ function EnumParam (props: Props): React.ReactElement<Props> {
|
||||
value: { [current[0].name as string]: value }
|
||||
});
|
||||
},
|
||||
[current]
|
||||
[current, onChange]
|
||||
);
|
||||
|
||||
if (isDisabled) {
|
||||
|
||||
@ -48,7 +48,7 @@ function KeyValue ({ className, isDisabled, label, onChange, onEnter, style, wit
|
||||
value.u8a
|
||||
)
|
||||
});
|
||||
}, [key, value]);
|
||||
}, [key, onChange, value]);
|
||||
|
||||
const _onChangeKey = useCallback(
|
||||
(key: string): void => setKey(createParam(key)),
|
||||
|
||||
@ -77,7 +77,7 @@ function KeyValueArray ({ className, defaultValue, isDisabled, isError, label, o
|
||||
|
||||
onChange && onChange(encoded);
|
||||
},
|
||||
[]
|
||||
[onChange, t]
|
||||
);
|
||||
|
||||
if (isDisabled) {
|
||||
|
||||
@ -12,7 +12,7 @@ function Null ({ onChange }: Props): React.ReactElement<Props> | null {
|
||||
isValid: true,
|
||||
value: null
|
||||
});
|
||||
}, []);
|
||||
}, [onChange]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ function Option ({ className, defaultValue, isDisabled, name, onChange, onEnter,
|
||||
isValid: true,
|
||||
value: null
|
||||
});
|
||||
}, [isActive]);
|
||||
}, [isActive, onChange]);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
|
||||
@ -29,10 +29,6 @@ function StructParam (props: Props): React.ReactElement<Props> {
|
||||
setParams((typeDef.sub as TypeDef[]).map((type): ParamDef => ({ name: type.name, type })));
|
||||
}, [type]);
|
||||
|
||||
if (isDisabled) {
|
||||
return <Static {...props} />;
|
||||
}
|
||||
|
||||
const _onChangeParams = useCallback(
|
||||
(values: RawParam[]): void => {
|
||||
onChange && onChange({
|
||||
@ -44,9 +40,13 @@ function StructParam (props: Props): React.ReactElement<Props> {
|
||||
}, {})
|
||||
});
|
||||
},
|
||||
[params]
|
||||
[params, onChange]
|
||||
);
|
||||
|
||||
if (isDisabled) {
|
||||
return <Static {...props} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='ui--Params-Struct'>
|
||||
<Base
|
||||
|
||||
@ -28,10 +28,6 @@ function Tuple (props: Props): React.ReactElement<Props> {
|
||||
}
|
||||
}, [type]);
|
||||
|
||||
if (isDisabled) {
|
||||
return <Static {...props} />;
|
||||
}
|
||||
|
||||
const _onChangeParams = useCallback(
|
||||
(values: RawParam[]): void => {
|
||||
onChange && onChange({
|
||||
@ -39,9 +35,13 @@ function Tuple (props: Props): React.ReactElement<Props> {
|
||||
value: values.map(({ value }): any => value)
|
||||
});
|
||||
},
|
||||
[]
|
||||
[onChange]
|
||||
);
|
||||
|
||||
if (isDisabled) {
|
||||
return <Static {...props} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='ui--Params-Tuple'>
|
||||
<Base
|
||||
|
||||
@ -78,7 +78,7 @@ function Vector ({ className, defaultValue, isDisabled = false, label, onChange,
|
||||
isValid: values.reduce((result: boolean, { isValid }): boolean => result && isValid, true),
|
||||
value: values.map(({ value }): any => value)
|
||||
});
|
||||
}, [values]);
|
||||
}, [values, onChange]);
|
||||
|
||||
const _rowAdd = useCallback(
|
||||
(): void => setCount((count) => count + 1),
|
||||
|
||||
@ -24,8 +24,9 @@ interface Props {
|
||||
|
||||
function ParamComp ({ defaultValue, index, isDisabled, name, onChange, onEnter, onEscape, overrides, type }: Props): React.ReactElement<Props> {
|
||||
const _onChange = useCallback(
|
||||
(value: RawParamOnChangeValue): void => onChange(index, value),
|
||||
[index]
|
||||
(value: RawParamOnChangeValue): void =>
|
||||
onChange(index, value),
|
||||
[index, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -74,6 +74,7 @@ function BlockAuthorsBase ({ children }: Props): React.ReactElement<Props> {
|
||||
}
|
||||
});
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@ -48,6 +48,7 @@ function EventsBase ({ children }: Props): React.ReactElement<Props> {
|
||||
}
|
||||
});
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@ -16,7 +16,7 @@ interface Props {
|
||||
}
|
||||
|
||||
function ContractCall ({ endowment, fees, onChange }: Props): React.ReactElement<Props> | null {
|
||||
const [state, setState] = useState<State>({
|
||||
const [, setState] = useState<State>({
|
||||
extraFees: new BN(0),
|
||||
extraAmount: new BN(0),
|
||||
extraWarn: false
|
||||
@ -34,12 +34,14 @@ function ContractCall ({ endowment, fees, onChange }: Props): React.ReactElement
|
||||
extraWarn: false
|
||||
};
|
||||
|
||||
if (!update.extraAmount.eq(state.extraAmount) || !update.extraFees.eq(state.extraFees)) {
|
||||
onChange(update);
|
||||
}
|
||||
setState((state): State => {
|
||||
if (!update.extraAmount.eq(state.extraAmount) || !update.extraFees.eq(state.extraFees)) {
|
||||
onChange(update);
|
||||
}
|
||||
|
||||
setState(update);
|
||||
}, [endowment, fees]);
|
||||
return update;
|
||||
});
|
||||
}, [endowment, fees, onChange]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ interface Props {
|
||||
}
|
||||
|
||||
function ContractDeploy ({ endowment, fees, onChange }: Props): React.ReactElement<Props> | null {
|
||||
const [state, setState] = useState<State>({
|
||||
const [, setState] = useState<State>({
|
||||
extraFees: new BN(0),
|
||||
extraAmount: new BN(0),
|
||||
extraWarn: false
|
||||
@ -34,12 +34,14 @@ function ContractDeploy ({ endowment, fees, onChange }: Props): React.ReactEleme
|
||||
extraWarn: false
|
||||
};
|
||||
|
||||
if (!update.extraAmount.eq(state.extraAmount) || !update.extraFees.eq(state.extraFees)) {
|
||||
onChange(update);
|
||||
}
|
||||
setState((state): State => {
|
||||
if (!update.extraAmount.eq(state.extraAmount) || !update.extraFees.eq(state.extraFees)) {
|
||||
onChange(update);
|
||||
}
|
||||
|
||||
setState(update);
|
||||
}, [endowment, fees]);
|
||||
return update;
|
||||
});
|
||||
}, [endowment, fees, onChange]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ function Proposal ({ deposit, onChange }: Props): React.ReactElement<Props> {
|
||||
isBelowMinimum
|
||||
});
|
||||
}
|
||||
}, [minDeposit]);
|
||||
}, [deposit, minDeposit, onChange]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -61,7 +61,7 @@ function Transfer ({ amount, fees, onChange, recipientId }: Props): React.ReactE
|
||||
isNoEffect
|
||||
});
|
||||
}
|
||||
}, [amount, allBalances, fees]);
|
||||
}, [amount, allBalances, fees, onChange]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user